security-headers

为处理敏感用户数据的网站推荐的安全标头:

推荐用于所有网站的安全标头:

网络上的已知威胁

在深入研究安全标头之前,先了解网络上的已知威胁以及为什么要使用这些安全标头。

保护您的网站免受注入漏洞

当您的应用程序处理的不受信任的数据会影响其行为并且通常会导致执行攻击者控制的脚本时,就会出现注入漏洞。由注入漏洞引起的最常见的漏洞是各种形式的跨站脚本(XSS),包括反射型 XSS、存储型 XSS、基于 DOM 的 XSS和其他变体。

XSS 漏洞通常可以让攻击者完全访问由应用程序处理的用户数据以及托管在同一Web 源中的任何其他信息。

针对注入的传统防御措施包括一致使用自动转义 HTML 模板系统,避免使用危险的 JavaScript API,以及通过在单独的域中托管文件上传和清理用户控制的 HTML 来正确处理用户数据。

  • 使用内容安全策略 (CSP)来控制您的应用程序可以执行哪些脚本以降低注入风险。
  • 使用X-Content-Type-Options可防止浏览器误解您网站资源的 MIME 类型,这会导致脚本执行。

将您的网站与其他网站隔离

网络的开放性允许网站以可能违反应用程序安全预期的方式相互交互。这包括意外发出经过身份验证的请求或在攻击者的文档中嵌入来自另一个应用程序的数据,从而允许攻击者修改或读取应用程序数据。

破坏 Web 隔离的常见漏洞包括点击劫持、跨站点请求伪造(CSRF)、跨站点脚本包含(XSSI) 和各种跨站点泄漏。

  • 使用X-Frame-Options防止您的文档被恶意网站嵌入。
  • 使用跨域资源策略 (CORP)来防止跨域网站包含您网站的资源。
  • 使用跨源开启器策略 (COOP)保护您网站的窗口免受恶意网站的交互。
  • 使用跨域资源共享 (CORS)来控制从跨域文档对您网站资源的访问。

安全地构建强大的网站

尽管有了同源策略(Same-origin policy)幽灵漏洞(Spectre)将加载到同一浏览上下文组中的任何数据都可能可读。浏览器限制了可能利用称为“跨域隔离”的特殊环境背后的漏洞的功能。通过跨域隔离,您可以使用强大的功能,例如SharedArrayBuffer

使用跨源嵌入器策略 (COEP)和COOP来启用跨源隔离。
加密您网站的流量#
当应用程序没有完全加密传输中的数据时,就会出现加密问题,从而允许窃听攻击者了解用户与应用程序的交互。

在以下情况下可能会出现加密不足:不使用 HTTPS、混合内容、设置没有Secure属性(或__Secure前缀)的cookie或松散的 CORS 验证逻辑。

使用HTTP 严格传输安全 (HSTS)通过 HTTPS 一致地提供您的内容。

Content Security Policy

XSS是一种利用网站上的漏洞允许注入和执行恶意脚本的攻击。
Content-Security-Policy 通过限制页面可以执行哪些脚本,提供了一个附加层来缓解 XSS 攻击。

如何使用 CSP

CSP 可以是针对 XSS 攻击的额外保护;您仍然应该确保转义用户输入。

两种方法可以启用 CSP。

  • 一种是通过 HTTP 头信息的Content-Security-Policy的字段
  • 另一种是通过网页的标签。
    两种方式的不同点在于:
  • meta标签的report-uri将会被忽略, 浏览器将不会发送CSP违规报告。
  • 无法使用 frame-ancestors 指令

推荐用法

使用基于随机数的严格 CSP

服务器配置文件

1
2
3
4
Content-Security-Policy:
script-src 'nonce-{RANDOM1}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';

nonce 是只使用一次的随机数。如果您可以为每个响应生成不同的随机数,则基于随机数的 CSP 才是安全的。如果您不能这样做,请改用基于哈希的 CSP。

在 HTML 中,为了加载脚本,请将nonce所有<script>标签的属性设置为相同的{RANDOM1}字符串。

index.html

1
2
3
4
<script nonce="{RANDOM1}" src="https://example.com/script1.js"></script>
<script nonce="{RANDOM1}">
// Inline scripts can be used with the `nonce` attribute.
</script>

使用基于散列(hash-based)的严格 CSP

如果您的 HTML 必须静态提供或缓存,例如,如果您正在构建单页应用程序,请使用基于哈希的严格 CSP。

服务器配置文件

1
2
3
4
Content-Security-Policy:
script-src 'sha256-{HASH1}' 'sha256-{HASH2}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';

在 HTML 中,您需要内联脚本以应用基于散列的策略,因为大多数浏览器不支持散列外部脚本。

1
2
3
4
5
6
7
// index.html
<script>
...// your script1, inlined
</script>
<script>
...// your script2, inlined
</script>

要加载外部脚本,请阅读选项 B:基于哈希的 CSP 响应头部分下的“动态加载源脚本” 。

配置report-uri,发送CSP拦截报告

1
Content-Security-Policy: report-uri <uri>;

指令report-uri指示用户代理报告违反内容安全策略的尝试。这些违规报告由通过 HTTP POST 请求发送到指定 URI 的 JSON 文档组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"csp-report": {
"document-uri": "http://localhost:3002/page1",
"referrer": "",
"violated-directive": "frame-src",
"effective-directive": "frame-src",
"original-policy": "script-src 'nonce-RANDOM1' 'unsafe-eval';child-src 'self'; report-uri http://localhost:3002/csp-report",
"disposition": "enforce",
"blocked-uri": "https://www.douban.com",
"line-number": 13,
"source-file": "http://localhost:3002/page1",
"status-code": 200,
"script-sample": ""
}
}

CSP工具

CSP Evaluator是是由谷歌推出的评估 CSP 的好工具,同时也是一个很好的基于 nonce 的严格 CSP 示例。
observatory.mozilla.org 是由Mozilla推出的web安全扫描工具。

输入网址点击 CHECK CSP按钮,即可解析出网页的Content Security Policy:

分析结果如下:

支持的浏览器

Chrome、Firefox、Edge、Safari

X-Content-Type-Options

X-Content-Type-Options HTTP 消息头相当于一个提示标志,被服务器用来提示客户端一定要遵循在 Content-Type 首部中对 MIME 类型 的设定,而不能对其进行修改。这就禁用了客户端的 MIME 类型嗅探行为,换句话说,也就是意味着网站管理员确定自己的设置没有问题。

语法

1
X-Content-Type-Options: nosniff

指令

nosniff
下面两种情况的请求将被阻止:

  • 请求类型是”style” 但是 MIME 类型不是 “text/css”,
  • 请求类型是”script” 但是 MIME 类型不是 JavaScript MIME 类型。

X-Frame-Options

如果恶意网站可以将您的网站作为 iframe 嵌入,这可能允许攻击者通过点击劫持来调用用户的意外操作。

X-Frame-Options指示浏览器是否应该被允许在渲染页面<frame><iframe><embed>,或<object>。建议所有文档都发这个头来表明是否允许被其他文档嵌入。

Content-Security-Policy HTTP 头中的 frame-ancestors 指令会替代这个非标准的 header。CSP 的 frame-ancestors 会在 Gecko 4.0 中支持,但是并不会被所有浏览器支持。然而 X-Frame-Options 是个已广泛支持的非官方标准,可以和 CSP 结合使用。

语法

X-Frame-Options 有三个可能的值:

1
2
3
X-Frame-Options: deny
X-Frame-Options: sameorigin
X-Frame-Options: allow-from https://example.com/

deny
表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
sameorigin
表示该页面可以在相同域名页面的 frame 中展示。
allow-from uri
表示该页面可以在指定来源的 frame 中展示。

设置 meta 标签是无效的!例如 没有任何效果。不要这样用!只有当像下面示例那样设置 HTTP 头 X-Frame-Options 才会生效。

Cross-Origin-Resource-Policy (CORP)

Cross-Origin-Opener-Policy (COOP)

攻击者的网站可以在弹出窗口中打开另一个站点,通过利用基于 Web 的跨站点泄漏来了解有关该站点的信息。在某些情况下,这也可能允许利用基于Spectre的侧信道攻击。

所述Cross-Origin-Opener-Policy头部提供为一个文件本身从通过打开跨源窗口隔离的方式window.open()或用一个链接target=”_blank”,而不rel=”noopener”。因此,文档的任何跨域开启者都将无法引用它,也无法与之交互。

语法

1
2
3
Cross-Origin-Opener-Policy: unsafe-none
Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Opener-Policy: same-origin

指令

unsafe-none
这是默认值。允许将文档添加到其打开程序的浏览上下文组,除非打开程序本身具有same-origin或的 COOP same-origin-allow-popups。

same-origin-allow-popups
保留对新打开的窗口或选项卡的引用,这些窗口或选项卡要么不设置 COOP,要么通过设置unsafe-none.

same-origin
将浏览上下文专门隔离到同源文档。跨源文档未加载到同一浏览上下文中。

noopener 属性有与 COOP 相同的效果,不同之处在于它只能工作从 opener 那边开始。(当第三方打开窗口时,你不能取消它的关联。)通过执行诸如 window.open(url, ‘_blank’, ‘noopener’) 或 ,

HTTP Strict Transport Security (HSTS)

是一个安全功能,它告诉浏览器只能通过HTTPS访问当前资源,而不是HTTP。

语法

1
2
3
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preload

指令

max-age=<expire-time>
设置在浏览器收到这个请求后的秒的时间内凡是访问这个域名下的请求都使用HTTPS请求。
includeSubDomains 可选
如果这个可选的参数被指定,那么说明此规则也适用于该网站的所有子域名。
preload 可选
查看 预加载 HSTS 获得详情。不是标准的一部分。

示例场景

你连接到一个免费WiFi接入点,然后开始浏览网站,访问你的网上银行,查看你的支出,并且支付一些订单。很不幸,你接入的WiFi实际上是黑客的笔记本热点,他们拦截了你最初的HTTP请求,然后跳转到一个你银行网站一模一样的钓鱼网站。 现在,你的隐私数据暴露给黑客了。

Strict Transport Security解决了这个问题;只要你通过HTTPS请求访问银行网站,并且银行网站配置好Strict Transport Security,你的浏览器知道自动使用HTTPS请求,这可以阻止黑客的中间人攻击的把戏。

alexa topsites获取全球访问量最高的50个网站,并对其进行分析。

参考链接

Security headers quick reference
Content-Security-Policy - HTTP | MDN
Content Security Policy Reference
Why you need “cross-origin isolated” for powerful features
Content-Security-Policy Meta http-equiv Example
X-Content-Type-Options
Strict-Transport-Security