Rudra Analyzer

安全

什么是安全响应头?逐项说明作用与添加方法

安全响应头用来开启每个浏览器都内置的防护功能。本文逐项说明它们的作用、稳妥的初始取值,以及在常见服务器和平台上的添加方法。

作者:Rudra Techno Team 阅读约 9 分钟
本页内容

安全响应头是一类 HTTP 响应头,用来告诉浏览器开启它本来就内置的防护功能:只通过 HTTPS 连接、拒绝运行来源不明的脚本、不允许其他网站用框架嵌入本页面,等等。它们修不好有漏洞的代码,但能让跨站脚本、点击劫持、协议降级这几类常见攻击更难得手,或者在攻击发生时把损失降下来。

如何查看你的网站发送了哪些响应头

  • 在浏览器中:打开开发者工具,切换到 Network 面板,刷新页面,点击第一个(文档)请求,查看 Response Headers。
  • 在终端中:curl -I https://example.com 会打印出响应头。
  • 使用检测工具:Rudra 的免费网站安全检测工具会检查网站是否使用 HTTPS 并把明文 HTTP 重定向过去,TLS 证书是否有效且没有临近过期,并读取 HSTS、Content-Security-Policy、X-Frame-Options(或 CSP frame-ancestors)、X-Content-Type-Options、Referrer-Policy 和 Permissions-Policy 的取值。它还会标出暴露软件版本号的 Server 或 X-Powered-By 响应头,并检查 Cookie 标志和 CORS。

光看有没有,下不了结论。一个允许从任何地方加载脚本的 Content-Security-Policy 虽然存在,却几乎起不到保护作用,所以 Rudra 还会解析具体取值:HSTS 的 max-age 短于 180 天、CSP 的 script-src 中含有 'unsafe-inline' 或 *,或者 X-Frame-Options 的值无效,都会被报告为配置薄弱。请阅读下面各节,对照判断你自己的取值。

逐个了解这些响应头

Strict-Transport-Security (HSTS)

告诉浏览器在一段指定时间内对你的域名一律使用 HTTPS,即使有人输入的是 http:// 或点击了旧链接也不例外。这样就堵住了一个空档:否则,身处同一网络的攻击者有机会拦截第一个不安全的请求。常见的取值是 Strict-Transport-Security: max-age=63072000; includeSubDomains(两年)。

  • 先用较短的 max-age(比如 300 秒),等确认每个页面都能通过 HTTPS 正常访问后再调大。
  • 只有当所有子域名都提供 HTTPS 时才添加 includeSubDomains,包括那些你可能早已忘记的旧子域名。
  • 加上 preload 标志并提交到浏览器预加载列表后,浏览器会把你的域名必须使用 HTTPS 这一点硬编码进去。撤销起来既麻烦又缓慢,所以要放在最后,想清楚了再加。
  • 浏览器只认通过 HTTPS 收到的 HSTS。

Content-Security-Policy (CSP)

一份清单,规定页面可以从哪些地方加载脚本、样式、图片、字体和框架。即使攻击者设法注入了脚本,一份好的 CSP 也能阻止浏览器运行它。它是这里功能最强的响应头,也是最容易配错的一个。对于简单的网站,可以从下面这个比较合理的配置起步:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'

它只允许加载来自你自己源的资源(外加内联的 data: 图片),禁用插件,阻止被注入的 <base> 标签改变相对网址的指向,并禁止页面被嵌入框架。真实的网站会用到统计分析、字体、嵌入内容和支付服务商,每一项都必须明确放行;内联脚本也会被拦截,除非你用 nonce 或哈希放行。上线时先使用 Content-Security-Policy-Report-Only:浏览器会在控制台中报告违规情况(如果你配置了上报端点,也会发送到那里),但不会拦截任何内容,这样你就可以在强制执行之前调整策略。

X-Content-Type-Options

X-Content-Type-Options: nosniff 会阻止浏览器猜测文件类型,比如把一个上传的文本文件当作脚本来执行。它没有可配置的选项,而且只要你的服务器发送了正确的 Content-Type 响应头,就几乎不会造成任何问题。建议所有地方都加上。

点击劫持防护:frame-ancestors 与 X-Frame-Options

点击劫持是把你的网站藏在另一个网站的框架里,诱骗用户点击你网站上的某个东西。现代的控制手段是 CSP 指令 frame-ancestors 'none'(一律不允许嵌入)或 frame-ancestors 'self'(只允许你自己的页面嵌入)。较早的 X-Frame-Options: DENY 或 SAMEORIGIN 在旧版浏览器中起同样的作用,两者同时发送也没有坏处。如果你的页面必须嵌入到特定合作伙伴的网站上,就把这些源列在 frame-ancestors 中。

Referrer-Policy

控制访客点击链接或加载资源时,把当前网址的多少内容发送给其他网站。网址中可能包含搜索词、ID 或令牌,这些都是你不希望泄露的。Referrer-Policy: strict-origin-when-cross-origin 在站内发送完整网址,对其他网站只发送源(https://example.com),从 HTTPS 跳到 HTTP 时则什么都不发。现代浏览器已经把它作为默认值;显式设置可以让行为保持一致。

Permissions-Policy

关闭你的网站用不到的强大浏览器功能,这样无论是你自己的代码还是嵌入的第三方内容,都无法请求使用它们。对于不需要摄像头、麦克风或定位的网站:Permissions-Policy: camera=(), microphone=(), geolocation=()。

应当移除或无需设置的响应头

  • 暴露版本的响应头。带版本号的 Server 和 X-Powered-By 等于明确告诉攻击者该针对哪款软件的哪个版本下手。把它们移除,或去掉版本号(在 nginx 中使用 server_tokens off;)。
  • X-XSS-Protection 控制的过滤器已被现代浏览器移除。不要依赖它;可以不发送,或者把它设为 0。

如何添加安全响应头

nginx

  • add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
  • add_header X-Content-Type-Options "nosniff" always;
  • add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  • add_header X-Frame-Options "DENY" always;

always 标志会让错误响应也带上这个响应头。要当心继承问题:只要某个 location 块里出现了任何一条 add_header 指令,它就不再继承 server 层级定义的那些,于是这些网址上的响应头会悄无声息地消失。要么在块内重复一遍,要么把它们放在一个公共文件里,再用 include 引入。

Apache

启用 mod_headers 后,在虚拟主机配置或 .htaccess 中写入:Header always set X-Content-Type-Options "nosniff",其他响应头也照此格式添加,例如 Header always set Referrer-Policy "strict-origin-when-cross-origin"。

Next.js

在 next.config.js 中为所有路径返回这些响应头:async headers() { return [{ source: '/:path*', headers: [{ key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }] }]; }。如果要使用带 nonce 的 CSP,则应按照 Next.js 文档的说明,改为在中间件中为每个请求生成该响应头。

Netlify、Cloudflare Pages 等托管平台

这类平台会读取发布目录中的 _headers 文件。先在一行写上路径匹配规则,例如 /*,再在下方缩进,每行写一个响应头,例如 X-Content-Type-Options: nosniff。在 Vercel 上,对应的是 vercel.json 中的 headers 部分。如果你使用了 CDN,通常也可以由它来添加响应头。

稳妥地上线

  1. 先添加低风险的响应头:X-Content-Type-Options、Referrer-Policy、Permissions-Policy 以及点击劫持防护。
  2. 用较短的 max-age 启用 HSTS,然后在几周内逐步调大。
  3. 以仅报告模式部署 CSP,修复它报告的问题,然后再强制执行。
  4. 测试涉及第三方的流程:登录、结账和支付框架、嵌入的视频、地图、聊天组件和统计分析。
  5. 用安全检测工具复查一遍;以后每次新增第三方工具时,都再查一次。

常见错误

  • 从别的网站照搬一份严格的 CSP,结果把支付、统计分析或嵌入内容弄坏了。
  • 写出一份满是 'unsafe-inline'、'unsafe-eval' 和通配符的 CSP,能通过“是否存在”的检查,却起不到多少保护作用。
  • 还没等所有子域名都做好 HTTPS 准备,就加上了 HSTS preload。
  • 在 HTML 的 <meta> 标签里设置响应头。只有部分 CSP 指令可以这样用;HSTS、frame-ancestors 和 X-Frame-Options 写在 meta 标签里会被忽略。
  • 在应用和代理两处都添加了响应头,导致响应中出现重复或互相冲突的值。
  • 把响应头当成更新软件、校验输入和转义输出的替代品。

响应头和响应头检查覆盖不到的地方

响应头只是其中一层防护。响应头检查发现不了过时的插件、弱的管理员密码、注入漏洞或暴露在外的备份文件。Cookie 也值得单独检查:会话 Cookie 应带有 Secure、HttpOnly 和 SameSite 属性。Rudra 的安全检测工具会报告页面自身响应所设置 Cookie 的这些标志(只报告名称,绝不涉及值);详情请参阅如何检查 Cookie 安全性,而被动检查能告诉你什么、不能告诉你什么,请参阅如何检查网站安全。如果想更全面地了解网站的健康状况,可以运行一次完整的网站审核。

常见问题

应该先添加哪个安全响应头?

先确保整个网站都通过 HTTPS 提供服务,然后添加 X-Content-Type-Options: nosniff、Referrer-Policy 和点击劫持防护,它们几乎不会造成任何问题。接着以较短的 max-age 添加 HSTS,最后才是 Content-Security-Policy,并且要先用仅报告模式。

安全响应头会影响 SEO 吗?

不会直接影响。HTTPS 本身是 Google 的一个轻量级排名信号,但 CSP 或 X-Content-Type-Options 这类响应头并不影响排名。它们保护的是你的访客和网站声誉,这一点更重要。

X-Frame-Options 过时了吗?

它已被更灵活的 CSP frame-ancestors 指令取代。两者同时发送没有坏处,还能照顾到旧版浏览器。

可以用 meta 标签设置安全响应头吗?

只能部分实现。Content-Security-Policy 可以写在 meta 标签里,但无法使用 frame-ancestors 或上报功能。HSTS、X-Frame-Options 和其他大多数安全响应头只有作为真正的 HTTP 响应头才会生效。

严格的 Content-Security-Policy 会把我的网站弄坏吗?

有可能,如果它拦截了页面所需的脚本、样式或框架。所以应当先从 Content-Security-Policy-Report-Only 开始,查看报告的违规情况,调整策略,然后再强制执行。

需要更深入的网站分析?

查看网站上的每个问题,并按修复优先级排序

无需账户即可免费检查任意页面,注册后还可抓取整个网站。每份报告涵盖 SEO、常见性能问题、无障碍和安全,并为每个问题提供修复建议。