重要的 HTTP 安全标头:CSP、HSTS、X-Content-Type-Options、X-Frame-Options 和 Referrer-Policy 详解

HTTP 响应标头为何对安全性至关重要

每个 HTTP 响应都带有标头——在主体之前发送的元数据行,控制浏览器如何处理内容。大多数标头很普通(Content-Type、Cache-Control),但一组特定的安全标头会指示浏览器执行限制,从而阻止整个攻击类别。这些不是理论上的加固措施;它们是针对反复出现在 OWASP Top 10 和 CVE 数据库中的攻击类别的实际防御。设置它们对性能几乎没有影响——每个响应大约增加 200–400 字节——而 securityheaders.com 等工具会对任何公开 URL 进行评分,让你立即看到缺少哪些标头。

关键洞察是安全标头由浏览器而非服务器执行。服务器宣布策略;浏览器执行它。这意味着高流量网站上的标头配置错误会同时影响每位访客的浏览器。反之,添加正确的标头可以在单次部署中修正多年积累的风险。此处介绍的五个标头——CSP、HSTS、X-Content-Type-Options、X-Frame-Options 和 Referrer-Policy——每个都针对攻击者可能利用的不同浏览器行为。

Content-Security-Policy:XSS 防火墙

跨站脚本攻击(XSS)是 OWASP Top 10 中最常见的 Web 漏洞。能够在你的 HTML 中注入 <script> 标签的攻击者可以窃取会话 Cookie、重定向用户或窃取表单数据。Content-Security-Policy(CSP)是一种基于白名单的标头,告诉浏览器哪些来源对脚本、样式、图片、字体和其他资源类型是可信任的。指令 Content-Security-Policy: default-src 'self' 只允许来自相同来源的资源,阻止所有外部脚本和内联 <script> 块。script-src 指令专门为 JavaScript 覆盖默认值。

最大的 CSP 陷阱是 unsafe-inline 关键字。添加 script-src 'unsafe-inline' 会重新启用内联脚本,完全破坏 CSP 对 XSS 的保护,但这却是网站启用 CSP 后出现问题时最常用的解决方法。正确的替代方案是 nonce:每次页面加载生成一个加密随机值,以 script-src 'nonce-r4nd0m' 包含在标头中,并以 nonce="r4nd0m" 包含在每个合法的 <script> 标签上。没有匹配 nonce 的注入脚本即使内联出现也会被阻止。对于尚未准备好严格策略的团队,Content-Security-Policy-Report-Only 会将违规报告发送到收集端点而不阻止任何内容——这是在执行策略之前衡量其影响的安全方式。

HTTP Strict Transport Security:防止 HTTPS 降级攻击

SSL 剥离是 2009 年 Black Hat 大会上公开演示的降级攻击。路径上的主动攻击者拦截用户的初始 HTTP 请求(在任何重定向到 HTTPS 之前),并将其作为纯 HTTP 代理到源站,同时向用户提供加密连接。用户在某些浏览器中看到挂锁,但攻击者与服务器之间的连接是未加密的——凭据和会话 Cookie 以明文可见。HTTP Strict Transport Security(HSTS)通过指示浏览器完全拒绝域名的 HTTP 连接来关闭此漏洞,期间由 max-age 指令以秒为单位定义。

典型的强 HSTS 标头是 Strict-Transport-Security: max-age=31536000; includeSubDomains; preload。31536000 秒的 max-age 是一年——纳入 HSTS 预加载列表所需的最低要求。includeSubDomains 将策略扩展到每个子域名。preload 表示有意加入 hstspreload.org 上的浏览器内置预加载列表;一旦列入该列表,即使在首次访问网站并收到标头之前,浏览器也会拒绝 HTTP 连接。关键限制:HSTS 必须通过有效的 HTTPS 响应传递。通过 HTTP 发送的标头将被忽略。部署 includeSubDomains 前先进行测试——如果任何子域名缺少有效的 TLS 证书,用户在整个 max-age 期间将被锁定无法访问。

X-Content-Type-Options 和 X-Frame-Options:两个单值安全提升

MIME 嗅探是一种遗留行为,某些浏览器会检查响应的前几个字节来猜测其内容类型,覆盖 Content-Type 标头。Internet Explorer 是最积极的 MIME 嗅探器;如果前几个字节看起来像标记语言,它会将 .jpg 作为 HTML 执行。能够向你的网站上传内容(图片、日志文件)的攻击者可以制作在浏览器嗅探时被作为脚本执行的有效载荷。X-Content-Type-Options: nosniff 指示所有现代浏览器信任声明的 Content-Type 并完全跳过嗅探。它只有一个有效值——nosniff——没有理由不在每个响应上设置它。

点击劫持将你的网站嵌入攻击者控制页面上的 <iframe> 中,然后覆盖透明按钮,使用户在不知情的情况下点击你网站上的 UI 元素。X-Frame-Options: DENY 阻止任何人框架你的页面;X-Frame-Options: SAMEORIGIN 只允许来自相同来源的页面框架。ALLOW-FROM uri 变体已过时,当前浏览器不支持。现代等效项是 CSP frame-ancestors 指令(frame-ancestors 'none' 对应 DENY,frame-ancestors 'self' 对应 SAMEORIGIN),它还支持多个允许的来源。由于不是所有浏览器在所有上下文中都检查 frame-ancestors,同时设置 X-Frame-Optionsframe-ancestors CSP 指令可提供深度防御。

Referrer-Policy:通过 Referer 标头控制信息泄露

HTTP Referer 标头(由于 RFC 中的历史拼写错误,只有一个"r")将用户导航来源页面的完整 URL 发送到该页面加载的每个外部资源——图片、脚本、样式表以及链接点击的目标。如果已登录的用户访问 /account/reset?token=eyJ...,而该页面加载了第三方分析脚本,则该脚本的请求在 Referer 标头中携带完整 URL,包括重置令牌。通过 referrer 泄露令牌是有据可查的漏洞类别;GitHub 在 2020 年修补了一个基于 referrer 的令牌暴露问题。

Referrer-Policy 标头取代了早期的 Meta referrer 机制和 Referer 标头抑制解决方法。策略 strict-origin-when-cross-origin 是推荐的默认值:对于同源请求发送完整 URL(对内部分析有用),但对于跨源请求只发送裸露的来源(https://example.com),从 HTTPS 导航到 HTTP 时不发送任何内容。no-referrer 完全不发送任何内容,最大化隐私但会破壞依赖 referrer 的分析。unsafe-url 始终发送完整 URL,包括路径和查询字符串——2020 年之前的浏览器默认值,也是大多数历史 referrer 泄露的来源。Chrome、Firefox 和 Safari 都在 2020–2021 年将 strict-origin-when-cross-origin 作为没有策略标头时的默认值,但明确设置它可确保跨所有浏览器版本的一致行为。

任何网站的实用安全标头基准

对于静态网站或简单的 Web 应用程序,四个标头以最小的配置复杂性覆盖最常见的攻击向量:X-Content-Type-Options: nosniff(始终,一行);X-Frame-Options: SAMEORIGIN(除非你的网站需要合法地被嵌入);Referrer-Policy: strict-origin-when-cross-origin;以及 Strict-Transport-Security: max-age=31536000; includeSubDomains(一旦确认每个子域名都是 HTTPS)。CSP 需要更多规划——从 Content-Security-Policy-Report-Only 和报告端点开始,收集几天的违规报告,然后编写只允许你实际使用的策略。

标头配置位置取决于你的技术栈:Nginx 在服务器块中使用 add_header 指令;Apache 在 httpd.conf.htaccess 中使用 Header always set;Vercel、Netlify 和 Cloudflare Pages 各有标头配置文件或仪表板设置。大多数 CDN 允许在边缘注入标头,这比应用程序级别标头更好,因为即使对于缓存响应,CDN 也能传递它们。TeaFun HTTP 标头检查工具可获取任何公开 URL 并显示存在哪些安全标头、其值以及每个标头功能的快速参考说明——在部署标头更改之前和之后使用它来审核网站。