HTTP安全响应头详解:HSTS、CSP及其他
HSTS、CSP、X-Frame-Options、Referrer-Policy和Permissions-Policy有什么作用,应该设置什么值,以及为什么标为“需检查”的响应头不一定是漏洞。
什么是安全响应头
每个HTTP响应都以响应头开头,即Content-Type: text/html这样的名称与值对。其中少数几个会改变浏览器处理页面的方式。它们发送起来没有成本,对每位访问者都生效,并且在服务器、托管平台或内容分发网络上设置,而不是写在页面的HTML中。
IPNixa在_headers文件中设置自己的响应头,由Cloudflare随每个页面一起发送。本站的检测工具会从我们的服务器请求你输入的地址,最多跟随三次重定向,并读取最终响应的响应头。
最重要的六个安全响应头
以下是检测工具评估的响应头。表中的值是常见且安全的起点,请根据网站的实际需要进行调整。
| 响应头 | 防范的问题 | 常用值 |
|---|---|---|
| Strict-Transport-Security (HSTS) | 首次访问后,连接被降级为未加密的HTTP | max-age=31536000; includeSubDomains |
| Content-Security-Policy (CSP) | 注入的脚本被执行,借此减少跨站脚本攻击 | default-src 'self'; script-src 'self'; frame-ancestors 'none' |
| X-Frame-Options或CSP frame-ancestors | 其他网站在隐藏框架中加载该页面(点击劫持) | DENY,或frame-ancestors 'none' |
| X-Content-Type-Options | 浏览器猜测文件类型,把数据当作脚本执行 | nosniff |
| Referrer-Policy | 完整的页面地址(包括路径和查询字符串)泄露给其他网站 | strict-origin-when-cross-origin |
| Permissions-Policy | 页面或其嵌入的框架使用摄像头、麦克风或位置信息 | camera=(), microphone=(), geolocation=() |
IPNixa检测工具如何评估各响应头
每个响应头会得到三种状态之一:存在、需检查或未提供。“需检查”表示该响应头存在,但其值不符合下列规则;这是提醒你仔细查看,而不是安全评分。
- HSTS只出现一次且max-age至少为15552000秒(180天)时,判定为“存在”。max-age更短或为0时,标为“需检查”。
- CSP通过script-src或default-src控制脚本,且整个策略中不含'unsafe-inline'、'unsafe-eval'或单独的*时,判定为“存在”。仅报告(report-only)策略会列为“未提供”,因为它不会拦截任何内容。
- 框架控制:frame-ancestors指令仅限于'none'、'self'或确切地址,或者X-Frame-Options设为DENY或SAMEORIGIN时,判定为“存在”。
- X-Content-Type-Options仅在值为nosniff时判定为“存在”。
- Referrer-Policy的最后一个值为no-referrer、same-origin、strict-origin或strict-origin-when-cross-origin时,判定为“存在”。浏览器会采用其能识别的最后一个值,因此列表按最后一项判定。
- 只要发送了Permissions-Policy,就判定为“存在”,因为合适的功能列表取决于具体网站。
真实网站发送了什么:样本
2026年11月2日,我们使用与检测工具相同的用户代理请求了五个网站的首页,并按IPNixa的评估规则分析了响应。响应头经常变化,请将结果视为某一时刻的快照。
| 网站 | HSTS | CSP | 框架控制 | nosniff | Referrer | Permissions |
|---|---|---|---|---|---|---|
| github.com | 存在 | 需检查 | 存在 | 存在 | 存在 | 未提供 |
| ipnixa.com | 存在 | 需检查 | 存在 | 存在 | 存在 | 存在 |
| www.wikipedia.org | 存在 | 未提供 | 未提供 | 未提供 | 未提供 | 未提供 |
| www.google.com | 未提供 | 仅报告 | 存在 | 未提供 | 未提供 | 未提供 |
| example.com | 未提供 | 未提供 | 未提供 | 未提供 | 未提供 | 未提供 |
解读样本:“需检查”不等于薄弱
GitHub的策略将脚本限制为其自有资源主机和特定哈希,相当严格。它之所以被标为“需检查”,只是因为其style-src允许'unsafe-inline',而该规则会在整个策略中查找这个关键字。内联样式的风险远小于内联脚本,因此这是有意的权衡,而不是漏洞。
我们自己的网站被标为“需检查”则另有原因:其策略只包含frame-ancestors 'none',能阻止页面被嵌入框架,但不限制脚本。我们在此如实列出这一结果。
Google首页以Content-Security-Policy-Report-Only发送其脚本策略,只报告违规而不拦截;这次响应中也没有发送HSTS,大型网站可以通过浏览器预加载列表等其他方式强制使用HTTPS。Wikipedia门户页发送了非常长的HSTS max-age,此外该页面几乎没有发送其他响应头。这些都不能说明这些网站整体上是否安全,只能显示每个响应要求浏览器启用哪些防护措施。
为自己的网站添加安全响应头
每次只添加一个响应头,重新加载网站,确认页面、登录、嵌入视频和支付表单仍能正常使用,再添加下一个。
- 先添加低风险的响应头:X-Content-Type-Options: nosniff、Referrer-Policy: strict-origin-when-cross-origin和X-Frame-Options: DENY(除非你的页面必须被其他网站嵌入框架)。
- 添加HSTS时先使用较短的max-age(例如300),确认所有子域名都能通过HTTPS正常访问后,再提高到31536000。只有所有子域名都支持HTTPS时,才添加includeSubDomains。
- 编写Content-Security-Policy,并先以Content-Security-Policy-Report-Only发送。收集违规报告或观察浏览器控制台,修复或放行合法来源,然后切换为强制执行的响应头。
- 添加Permissions-Policy,列出你不使用的高权限功能,例如camera=(), microphone=(), geolocation=()。
- 在nginx上使用带always参数的add_header,在Apache上使用Header always set,在静态托管或CDN上使用平台的headers文件或响应头规则。
- 再次运行IPNixa响应头检测。对于每个缺失的响应头,它都会给出可直接复制到nginx、Apache或_headers文件中的配置行。
HSTS预加载及其他注意事项
HSTS预加载列表内置于浏览器中,因此即使是首次访问也会使用HTTPS。提交域名需要至少一年的max-age、includeSubDomains和preload指令,而从列表中移除需要数月才能生效到用户端。只有在所有子域名(包括内部子域名)都将一直使用HTTPS时,才应提交。
X-XSS-Protection已经过时;现代浏览器会忽略它,其作用已由CSP取代。跨源隔离响应头(Cross-Origin-Opener-Policy、Cross-Origin-Embedder-Policy和Cross-Origin-Resource-Policy)不在检测工具的评估范围内,因为只有使用共享内存等功能的网站才需要它们,而启用它们可能会阻止第三方嵌入内容。
常见问题
缺少某个响应头,是否意味着访问该网站不安全?
不代表。响应头在浏览器端增加针对特定攻击的防护。没有这些响应头的网站仍可能运营良好,全部具备的网站也可能存在其他缺陷。请把结果当作网站所有者的检查清单,而不是给访问者的结论。
为什么浏览器显示的响应头,检测工具却没有看到?
网站可能按页面、国家/地区、登录状态或CDN节点返回不同的响应头。检测工具只读取你输入的地址的一次响应,该响应从Cloudflare网络获取,不带Cookie。
应该使用X-Frame-Options还是frame-ancestors?
CSP中的frame-ancestors是现代的控制方式,可以只允许特定网站嵌入。同时发送X-Frame-Options可兼顾旧版浏览器;两者同时存在时,当前浏览器以frame-ancestors为准。
可以检查需要登录的页面吗?
不能。检测工具不发送Cookie或凭据,也不检查私有地址和本地地址。对于需要登录或位于私有网络中的页面,请从浏览器开发者工具或curl -I复制原始响应头,粘贴到检测工具中;粘贴的响应头在你的浏览器中分析。