最全 HTTP 安全响应头设置指南
销售“安全记分卡”的公司正在崛起,并已开始成为企业销售的一个因素。这些公司组合使用 HTTP 安全报头和 IP 信誉来进行评级。不过,在很大程度上,公司的得分取决于对外开放网站上设置的安全响应报头。本文介绍了常用的安全响应报头及对应的推荐安全值,并给出了示例。
销售“安全记分卡”的公司正在崛起,并已开始成为企业销售的一个因素。我从客户那里了解到,他们对从评级低的供应商那里的采购很不放心,至少有案例表明,他们依据最初的评级改变了采购决策。
我调查了这些评级公司是如何计算公司安全性得分的,结果发现他们组合使用了 HTTP 安全报头和 IP 信誉。
IP 信誉基于的是黑名单和垃圾邮件列表,再加上公共 IP 所有权数据。只要你的公司没有垃圾邮件,并且能够快速检测和阻止恶意软件感染,那么通常这些软件应该就是干净的。HTTP 安全报头使用的计算方式与Mozilla Observatory的工作方式类似。
因此,对于大多数公司来说,在很大程度上,他们的得分取决于对外开放的网站上设置的安全响应报头。
设置正确的响应报头可以快速实现(通常不需要进行大量测试),并能提高网站的安全性,现在还可以帮助我们赢得具有安全意识的客户。
我对这种测试方法的价值以及这些公司提出的过高的定价方案持怀疑态度。我不认为它与真正的产品安全性有那么大的关联。然而,这无疑增加了设置响应报头并维护其正确性的重要性,值得为此投入时间。
在本文中,我将介绍常用的评估响应报头,及每个报头的推荐安全值,并给出一个响应报头设置的示例。在本文的最后,还将给出常见的应用程序和 Web 服务器的设置示例。
重要的安全响应报头
Content-Security-Policy(CSP)
CSP 通过指定允许加载哪些资源的形式,来防止跨站脚本注入。在本文所列的安全响应报头中,正确地设置和维护 CSP,可能是最耗时的,也是最容易出现风险的。在开发 CSP 的过程中,要谨慎充分地测试它——以“合法”的方式阻塞站点使用的内容源会破坏站点的功能。
创建 CSP 初稿的一个很好的工具是Mozilla 实验室的 CSP 浏览器扩展。在浏览器中安装此扩展程序,首先充分地浏览要为其设置 CSP 的站点,然后在站点中使用生成的 CSP。理想情况下,还可以重构 JavaScript,使其没有残留的任何内联脚本,从而使我们可以删除“unsafe inline”指令设置。
CSP 的指令设置可能比较复杂,也很混乱,因此,如果你想更深入的了解 CSP,请访问其官方网站。
一个好的 CSP 开始可能是如下这样的(在真正的站点上使用时,可能需要进行大量的修改)。在包含该站点的每个部分中都添加域名。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';
|
Strict-Transport-Security(HSTS)
该响应报头告诉浏览器,只能通过 HTTPS 访问网站——如果网站启用过 HTTPS,它将会一直生效。如果使用子域名,还建议在任何使用过的子域名对此加以强制。
|
X-Content-Type-Options
该响应报头确保浏览器遵守应用程序设置的 MIME 类型。这有助于防止某些类型的跨站脚本注入攻击。
它还能减少浏览器“猜测”某些内容不正确时的意外应用程序行为,例如,当开发人员将某个页面标记为“HTML”,但浏览器认为它更像 JavaScript,并试图将其渲染为 JavaScript 时。该响应报头能确保浏览器始终遵守服务端设置的 MIME 类型。
X-Content-Type-Options: nosniff
|
Cache-Control(缓存控制)
这个响应报头比其他的要稍微复杂一些,因为我们可能需要根据内容类型的不同而使用不同的缓存策略。
任何具有敏感数据的页面,如用户页面或客户结算页面,都应该设置成无缓存。其中一个原因是,防止共享计算机上的某个人按回退按钮或浏览历史记录又能查看到个人信息。
但是,对于像静态资产(图像、CSS 文件和 JS 文件)等很少变更的页面,很适合使用缓存。既可以通过逐页设置的方式来实现,也可以通过在服务端配置使用正则表达式的方式来实现。
# 默认情况不使用缓存
|
|
Header set Cache-Control no-cache
|
|
# 静态资产设置成缓存 1 天
|
|
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
|
|
Header set Cache-Control "max-age=86400, public"
|
|
</filesMatch>
|
Expires(过期时间)
该响应报头能设置当前请求缓存的过期时间。如果设置了 Cache-Control 的 max-age 响应报头,它将会被忽略,因此,在不考虑使用 Cache-Control 而进行本地缓存测试时,才设置它。
为了安全起见,我们假定浏览器不应该缓存任何内容,因此,我们可以把过期时间设置为一个总表示过期的数值。
Expires: 0
|
X-Frame-Options
该响应报头用来表明站点是否允许在 iFrame 中展示。
如果恶意站点将我们的网站嵌套在 iFrame 中,那么恶意站点就可以通过运行一些 JavaScript 来执行点击劫持攻击,这些 JavaScript 能够捕获 iFrame 上的鼠标点击事件,然后代表用户与该站点进行交互(不必单击需要单击它们的地方!)。
应该始终将它设置为 deny(拒绝),除非特别需要使用内嵌,在这种情况下,应将其设置为 same-origin(同源)。如果需要在页面中内嵌其他的站点,也可以在此处以白名单的形式列举其他的域名。
还应该注意的是,这个响应报头已经被 CSP 的 frame-ancestors 指令所取代。目前,我仍然建议设置该响应报头来兼容不同的工具,但将来它可能会被逐步淘汰。
X-Frame-Options: deny
|
Access-Control-Allow-Origin
通过该响应报头可以告诉浏览器,允许哪些其他站点的前端 JavaScript 代码对页面发出请求。除非需要设置此响应报头,否则通常默认值就是正确的设置。
例如,如果站点 A 使用了一些 JavaScript,该 JavaScript 想要向站点 B 发出请求,那么站点 B 必须使用指定了允许站点 A 发出此请求的报头来提供响应。如果需要设置多个源,请参见MDN 上的详情介绍页面。
这可能有点难以理解,因此,我画了一个图表来说明这个响应报头是如何工作的:
``` Access-Control-Allow-Origin: http://www.one.site.com ```
Set-Cookie
确保 cookie 仅能通过 HTTPS(加密)传送,并且不能通过 JavaScript 访问。如果站点也支持 HTTPS(站点应该支持 HTTPS),那么就只能发送 HTTPS cookie。我们通常需要设置如下标志:
-
Secure
-
HttpOnly
一个定义 Cookie 的示例:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
|
请参阅Mozilla 文档中的 cookies 部分以了解更多相关信息。
X-XSS-Protection
该响应报头用来指示浏览器停止执行跨站脚本攻击检测。一般来说,设置它的风险很低,但在投入生产前仍需要进行测试。
X-XSS-Protection: 1; mode=block
|
Web 服务器的配置示例
通常,最好在服务器配置中添加站点范围内的响应报头。在此,cookie 是一个例外,因为它们通常是在应用程序内定义的。
在将任何响应报头添加到站点之前,我建议首先检查 Observatory 或手动查看响应报头,以查看已经设置了哪些响应报头。有些框架和服务器会自动设置其中一些响应报头,因此,我们只需设置我们需要的或想要变更的响应报头即可。
Apache 配置
.htaccess 中的 Apache 设置示例:
<IfModule mod_headers.c>
|
|
## CSP
|
|
Header set Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';
|
|
## 通用的安全响应报头
|
|
Header set X-XSS-Protection: 1; mode=block
|
|
Header set Access-Control-Allow-Origin: http://www.one.site.com
|
|
Header set X-Frame-Options: deny
|
|
Header set X-Content-Type-Options: nosniff
|
|
Header set Strict-Transport-Security: max-age=3600; includeSubDomains
|
|
## 缓存策略
|
|
# 默认情况下不使用缓存
|
|
Header set Cache-Control no-cache
|
|
Header set Expires: 0
|
|
# 设置静态资产缓存 1 天
|
|
<filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$">
|
|
Header set Cache-Control "max-age=86400, public"
|
|
</filesMatch>
|
|
</IfModule>
|
Nginx 设置
## CSP
|
|
add_header Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';
|
|
## 通用的安全响应报头
|
|
add_header X-XSS-Protection: 1; mode=block;
|
|
add_header Access-Control-Allow-Origin: http://www.one.site.com;
|
|
add_header X-Frame-Options: deny;
|
|
add_header X-Content-Type-Options: nosniff;
|
|
add_header Strict-Transport-Security: max-age=3600; includeSubDomains;
|
|
## 缓存策略
|
|
** 默认不使用缓存 **
|
|
add_header Cache-Control no-cache;
|
|
add_header Expires: 0;
|
|
** 设置静态资产缓存 1 天 **
|
|
location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff|ttf|eot)$ {
|
|
try_files $uri @rewriteapp;
|
|
add_header Cache-Control "max-age=86400, public";
|
|
}
|
应用程序级的响应报头设置
如果我们没有访问 Web 服务器的权限,或者需要设置复杂的响应报头,那么我们就可能需要在应用程序内设置这些响应报头了。这通常可以在整个站点的框架中间件中实现,也可以在每次响应的基础上进行一次性的报头设置。
为了简便起见,在示例中,只包含了一个响应报头。所需的全部响应报头都是以相同的方式通过该方法来添加的。
Node 及 express:
添加一个全局挂载路径:
app.use(function(req, res, next) {
|
|
res.header('X-XSS-Protection', 1; mode=block);
|
|
next();
|
|
});
|
Java 及 Spring:
我没有太多的 Spring 实践经验,但Baeldung对在 Spring 中如何设置响应报头提供了很好的指导。
PHP:
我对各种 PHP 框架不是很熟悉。查找了能够处理请求的中间件。对于单个响应,它的设置非常简单。
header("X-XSS-Protection: 1; mode=block");
|
Python 及 Django
Django 包含可配置的安全中间件,通过该中间件来处理所有响应报头的设置。首先启用它们。
对于特定页面,可以将响应视为字典。Django 有一个处理缓存的特殊方法,如果试图以这种方式设置缓存响应报头,那么就应该调研后再使用。
response = HttpResponse()
|
|
response["X-XSS-Protection"] = "1; mode=block"
|
总结
设置响应报头相对来说比较简单快捷。在数据保护、跨站脚本注入和点击劫持方面,站点安全性将会有相当大的提高。
还可以确保我们不会因为依赖此信息的公司安全评级而失去未来的业务交易。这种做法似乎越来越多,我希望在未来几年,它能继续在企业销售中发挥作用。
如果以上有所遗漏,你认为还应该包含其他的安全响应报头,请留言回复。
英文原文:https://nullsweep.com/http-security-headers-a-complete-guide