使用 HTTP 标头保护您的网站
Mozilla 的 Observatory通过教导开发人员、系统管理员和安全专家如何安全地配置他们的网站来帮助网站。
让我们来看看 Observatory 对一个相当简单的Static Buildpack应用程序给出的分数,https://2017.keeprubyweird.com。
考试成绩
测试 | 经过 | 分数 | 解释 |
---|---|---|---|
内容安全策略 | ✗ | -25 | 内容安全策略(CSP)标头未实现 |
曲奇饼 | ― | 0 | 未检测到任何 Cookie |
跨域资源共享 | ✔ | 0 | 内容无法通过跨域资源共享 (CORS) 文件或标头看到 |
HTTP 公钥固定 | ― | 0 | HTTP 公钥固定(HPKP)标头未实现(可选) |
HTTP 严格传输安全 | ✗ | -20 | HTTP 严格传输安全 (HSTS) 标头未实现 |
重定向 | ✔ | 0 | 初始重定向到同一主机上的 https,最终目的地是 https |
推荐人政策 | ― | 0 | Referrer-Policy 标头未实现(可选) |
子资源完整性 | ― | 0 | 子资源完整性 (SRI) 未实现,但所有脚本均从相似的来源加载 |
X-Content-Type-选项 | ✗ | -5 | X-Content-Type-Options 标头未实现 |
X-Frame-选项 | ✗ | -20 | X-Frame-Options(XFO)标头未实现 |
X-XSS 保护 | ✗ | -10 | X-XSS-Protection 标头未实现 |
尽管我们使用 Heroku 的自动证书管理功能轻松为域名获取 SSL 证书,但我们的总体评分仅为 F,20/100。我们将逐一分析每个失败的测试,找出失败的原因,并尝试修复它们。
内容安全策略 (CSP)
这里的故障是“CSP 标头未实现”,当我们查看链接的安全指南时,我们发现 CSP 使我们能够控制网站上引用的脚本和资源的加载位置。对于 Keep Ruby Weird 来说,这意味着字体、多个外部图片源以及一些分析源。
CSP 为我们提供了几个严格级别:
default-src <source>
、、、等等script-src <source>
,object-src <source>
这些限制了各类资源的来源。https:
将指定类型的资源或所有资源限制为仅 HTTPShttps://example.com
将资源限制在此域内。可以为同一*-src
指令提供多个此类来源。'self'
意味着资源只能从当前主机加载,对于诸如的相关资源很有用<script src="/index.js">
。- 请参阅MDN 上的CSP: default-src了解完整选项。
- 默认情况下,标
Content-Security-Policy
头不允许<script>
使用带有内联代码的标签。带有 的标签src
则允许使用。可以通过添加 来禁用此'unsafe-inline'
功能,这会降低我们网站的安全性。您还可以指定nonce
这些脚本内容的 s 或 SHA 值,以允许它们执行。 frame-ancestors 'none'
防止您的网站被加载到 iframe 中并被用于点击劫持攻击。如果您确实需要在 iframe 中显示您的网站,您可以指定 URL。
为了实现我们的目的,我们希望能够使用自托管资源、来自 Twitter 和 AWS 的图片、来自 Google 和 Twitter 分析的脚本,以及来自 Google Fonts 的样式表和字体条目。我们还需要重新定义 'self'
一些指令,因为除非未指定选项,否则它们不会恢复为默认值。例如,我们没有指定 ,object-src
因此它会恢复为default-src
的值'self'
。
Content-Security-Policy: default-src 'self';
script-src https://static.ads-twitter.com https://www.google-analytics.com;
img-src 'self' https://s3.amazonaws.com https://twitter.com https://pbs.twimg.com;
font-src 'self' https://fonts.gstatic.com;
style-src 'self' https://fonts.googleapis.com;
frame-ancestors 'none';
我们可以将其添加到我们的 static.json 中作为所有路径的标头集合的一部分:
# ...
"headers": {
"/**": {
"Content-Security-Policy": "default-src 'self'; script-src https://static.ads-twitter.com https://www.google-analytics.com; img-src 'self' https://s3.amazonaws.com https://twitter.com https://pbs.twimg.com; font-src 'self' https://fonts.gstatic.com; style-src 'self' https://fonts.googleapis.com; frame-ancestors 'none';"
}
}
当我们添加或删除外部资源时,我们需要更新此集合,否则我们的用户将在浏览器的控制台中看到错误,并且资源将不可用。
HTTP 严格传输安全 (HSTS)
我们未能通过此测试,原因基本相同:“未实现 HTTP 严格传输安全 (HSTS) 标头”。
HSTS 会告知浏览器我们的网站只能通过 HTTPS 访问。查看 HSTS 安全指南,我们发现 HSTS 提供了几个非排他性标志:
max-age=<seconds>
用户代理重定向到 HTTPS 的时间长度(以秒为单位)。这会告诉浏览器“一旦您看到此信息,就假设所有到此域名的请求都将在这段时间内通过 HTTPS 进行”。Mozilla 建议使用 2 年(即63072000
2 秒)。此标志是必需的。includeSubDomains
用户代理是否应升级子域名的请求。除非您有理由不在所有子域名上使用 SSL,否则您可能需要此功能。即使您目前没有子域名,我也建议您这样做。preload
如果您启用此标记,并在Chrome HSTS 预加载列表中注册了您的域名,那么浏览器甚至无需查看此标头即可强制执行 HTTPS 请求。此功能很有用,但请自行选择,因为您需要在预加载列表中查找域名,勾选一些复选框并提交,才能注册您自己的域名。注册过程非常简单,我们将在这里进行操作。
# ...
"headers": {
"/**": {
"Strict-Transport-Security": "max-age=63072000; includeSubDomains; preload"
}
}
X-Content-Type-选项
此标头告知浏览器,如果服务器指示的 MIME 类型不正确,则不要加载脚本和样式表。启用此功能很有用。
# ...
"headers": {
"/**": {
"X-Content-Type-Options": "nosniff"
}
}
X-Frame-选项
此标头可防止您的网站在 iframe 中加载。它有助于防止“点击劫持”攻击。它与 Content-Security-Policy 提供的保护相同frame-ancestors 'none'
,但增加了对旧版浏览器的支持。如果您确实需要在网站其他页面的 iframe 中显示您的网站,则可以改用此SAMEORIGIN
选项。
# ...
"headers": {
"/**": {
"X-Frame-Options": "DENY"
}
}
X-XSS 保护
此标头可防止跨站点脚本(XSS)攻击。它提供与 Content-Security-Policy 类似的保护,但同样保护旧版浏览器。
# ...
"headers": {
"/**": {
"X-XSS-Protection": "1; mode=block"
}
}
整合起来
将此标题块添加到我们的 static.json 中,可将天文台的分数从 F 提高到 A。
"headers": {
"/**": {
"Content-Security-Policy": "default-src 'self'; script-src https://static.ads-twitter.com https://www.google-analytics.com 'sha256-q2sY7jlDS4SrxBg6oq/NBYk9XVSwDsterXWpH99SAn0='; img-src 'self' https://s3.amazonaws.com https://twitter.com https://pbs.twimg.com; font-src 'self' https://fonts.gstatic.com; style-src 'self' https://fonts.googleapis.com; frame-ancestors 'none';",
"Strict-Transport-Security": "max-age=63072000; includeSubDomains; preload",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block"
}
}
当我们加载浏览器时,一切看起来都正常!不幸的是,我们漏掉了一件事,我们可以在控制台中看到。
拒绝执行内联脚本,因为它违反了以下内容安全策略指令:“script-src https://static.ads-twitter.com https://www.google-analytics.com ”。启用内联执行需要“unsafe-inline”关键字、哈希值(“sha256-q2sY7jlDS4SrxBg6oq/NBYk9XVSwDsterXWpH99SAn0=”)或随机数(“nonce-...”)。
尽管我们在 script-src 中添加了https://www.google-analytics.com,但由于它是以内联脚本的形式加载的,因此我们需要明确允许它运行。错误消息很贴心地提供了几个选项:“需要‘unsafe-inline’关键字、哈希值(‘sha256-q2sY7jlDS4SrxBg6oq/NBYk9XVSwDsterXWpH99SAn0=’)或随机数(‘nonce-...’)才能启用内联执行。”
'unsafe-inline'
听起来,嗯,不安全,所以我们跳过这个。Nonce 是一次性使用的数字,允许内联脚本运行。Nonce 可以很安全,但由于我们讨论的是静态页面,这超出了我们的讨论范围。错误消息中提供的哈希值是内联代码块内容的实际 SHA-256 和,比其他选项更安全。它可以阻止攻击者更改 Google Analytics(分析)内联脚本的内容,这使得它比不受保护的内联脚本更安全。与更改外部依赖项一样,如果我们更改该脚本标签,我们也需要更改 SHA-256 和。
"Content-Security-Policy": "default-src 'self'; script-src https://static.ads-twitter.com https://www.google-analytics.com 'sha256-q2sY7jlDS4SrxBg6oq/NBYk9XVSwDsterXWpH99SAn0='; img-src 'self' https://s3.amazonaws.com https://twitter.com https://pbs.twimg.com; font-src 'self' https://fonts.gstatic.com; style-src 'self' https://fonts.googleapis.com; frame-ancestors 'none';"
我们已经添加了 SHA 总和,现在 Google Analytics 已全部设置完毕!
结果
测试 | 经过 | 分数 | 解释 |
---|---|---|---|
内容安全策略 | ✔ | +5 | 内容安全策略 (CSP) 实施无需'unsafe-inline' 或'unsafe-eval' |
曲奇饼 | ― | 0 | 未检测到任何 Cookie |
跨域资源共享 | ✔ | 0 | 内容无法通过跨域资源共享 (CORS) 文件或标头看到 |
HTTP 公钥固定 | ― | 0 | HTTP 公钥固定(HPKP)标头未实现(可选) |
HTTP 严格传输安全 | ✔ | 0 | HTTP 严格传输安全 (HSTS) 标头设置为至少六个月 (15768000) |
重定向 | ✔ | 0 | 初始重定向到同一主机上的 https,最终目的地是 https |
推荐人政策 | ✔ | 0 | Referrer-Policy 标头未实现(可选) |
子资源完整性 | ― | 0 | 子资源完整性 (SRI) 未实现,但所有脚本均从相似的来源加载 |
X-Content-Type-选项 | ✔ | 0 | X-Content-Type-Options 标头设置为"nosniff" |
X-Frame-选项 | ✔ | +5 | 通过 CSPframe-ancestors 指令实现的 X-Frame-Options (XFO) |
X-XSS 保护 | ✔ | 0 | X-XSS-Protection 标头设置为"1; mode=block" |
我们达到了 A+ 的评分!对于一个小时的工作来说,这已经很不错了,而且用户现在访问我们的网站时也更加安全了。
额外积分
我们现在进入最后冲刺阶段。我们可以采取一些可选措施来进一步增强安全性和隐私性。
推荐人政策
浏览器包含一个Referrer
标头,用于识别用户访问新页面时来自哪里。它有助于追踪用户来自哪里,但也存在一些隐私问题。Referrer-Policy
标头控制何时提供信息以及提供多少信息。
no-referrer
。告诉浏览器永远不要发送Referer
标头。same-origin
. 发送引荐来源网址,但仅限于网站内部的请求(例如 /security-in-the-static-buildpack => /posts)strict-origin
. 将引荐来源信息发送到所有来源,但仅发送不带路径的 URL(例如https://example.com/)strict-origin-when-cross-origin
. 发送同源的完整引用信息,但仅发送外部来源的无路径 URL。
no-referrer
可以用作浏览器的后备,因为其中许多选项目前尚未实现。
Referrer-Policy: no-referrer, strict-origin-when-cross-origin
更多的?
Mozilla Observatory 还对Cookies和子资源完整性进行了测试,但是在我们进行更改之后,它对 Keep Ruby Weird 网站感到满意,因此这些留给读者作为练习。
最终结果
这是此更改的最终结果,排除了对 HSTS 的选择加入“预加载”指令,这是我们对所有静态构建包应用程序的建议。
{
"headers": {
"/**": {
"Content-Security-Policy": "default-src 'self'; script-src https://static.ads-twitter.com https://www.google-analytics.com 'sha256-q2sY7jlDS4SrxBg6oq/NBYk9XVSwDsterXWpH99SAn0='; img-src 'self' https://s3.amazonaws.com https://twitter.com https://pbs.twimg.com; font-src 'self' https://fonts.gstatic.com; style-src 'self' https://fonts.googleapis.com; frame-ancestors 'none';",
"Referrer-Policy": "no-referrer, strict-origin-when-cross-origin",
"Strict-Transport-Security": "max-age=63072000; includeSubDomains",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block"
}
}
}