HTTP 安全头速览

书接上文——《JS 安全策略》,我们速览了一些 Web 开发中常见到的安全策略;本文继续展开,讲讲另一种Web防御策略——HTTP安全标头(Security Header)。

概述

HTTP Headers 是客户端和服务器在 HTTP 传输过程中添加的附属信息。一个请求头由名称(不区分大小写)后跟一个冒号:,再跟一个具体的值(不带换行符)组成。常见的 Http 头有好多种,如:

  • auth 类:Authorization,WWW-Authenticate,Proxy-Authenticate 等等
  • cache 类:Age,Expires,Cache-Control 等等
  • 条件请求类:If-Match,If-Modified-Since 等等

上面这些标头应该是耳熟能详了;不过,对于安全类的 HTTP 头,我问了周边的小伙伴,很多人都不熟悉;所以,这期就借机介绍几个非常实用的安全头,大家看看自己用过几个。

Trusted Types (可信任类型)

我们从之前提过的可信任类型说起。Trusted Types 主要用于防御基于 DOM 的跨站点脚本攻击(DOM-based XSS)。

DOM-based XSS: 一种将恶意数据传递给动态语言接收器的攻击手段;在 JS 语言中,最常被攻击的接收器就是: eval().innerHTML 了。

Trusted Types 就是一种黑名单机制,限制那些有安全隐患的 Web API 被浏览器调用。除此之外,主流的浏览器厂商现在都提供了一个特殊的对象来代替上述 API,人称 trustedTypes

启用 Trusted Types 的方法很简单,服务器返回的 HTTP 消息中带上如下安全头即可:

Content-Security-Policy: require-trusted-types-for 'script'

启用后,如下代码——修改.innerHTML——就会直接抛出异常了。

el.innerHTML = ""; // This throws an exception.

至于替代手段,就是利用 trustedTypes 生成的字符串绕开检查罢了。下例中,通过使用 trustedTypes,让开发人员有意识地把 inner html 中的 tag 标签——<>——替换为特殊符号(<>)以防止 XSS 注入。

const policy = trustedTypes.createPolicy("escapePolicy", {
  createHTML: (str) => {
    return str.replace(/\/g, ">");
  },
});

// accepted operation
const escaped = policy.createHTML("");
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'

Content Security Policy(CSP)

CSP 我们讲过很多次,我们再快速过一遍。CSP 主要用于阻止跨站点脚本执行的一种安全策略。通常建议所有的 Web 应用都开启 CSP:

  • 如果是服务端渲染的脚本,请至少启用 CSP 的 nonce 机制
  • 如果是静态资源器托管的脚本,请至少启用 CSP 的 Integrity 机制

X-Content-Type-Options

部分浏览器(如 IE < 8.0)会出现一种加 MIME 混淆的情况。举个通俗的例子,某些图片会包含一些合法的 HTML 标记(大家懂的),当那些浏览器加载这类图片时会执行里面的 HTML 脚本,引发所谓的基于 MIME 的脚本攻击。

但是,如果服务器响应标头里加上 X-Content-Type-Options: nosniff,这种攻击就可以避免了。因为此时的浏览器将强制要求加载的资源带上 MINE 类型描述——Content-Type,不然抛出异常,比如 IE 的异常如下:

SEC7112: Script from http://xxx.com was blocked due to mime type mismatch script.asp

而对于带有 MIME 类型的图片资源(如image/jpeg),浏览器将视作静态资源,就不会继续执行里面的脚本了。

X-Frame-Options

X-Frame-Options 顾名思义,和 iframe 嵌入相关的安全头了。部分网站可能会将你的网页当作 iframe 嵌入,然后劫持你的用户操作;一些不明真相的用户会被劫持的反馈引向不安全的网站,或是泄露敏感信息。这就是经典的 Spectre-type attacks(幽灵漏洞攻击)。

假如你不打算别人嵌入你的网站,那就带一个安全头 X-Frame-Options: DENY,它的