🔒 第05章 · OCSP/HSTS/安全头

security headers 加固

OCSP、HSTS 与安全响应头

这一章讲 HTTPS 站点"证书之外"的那层安全:如何让浏览器更快更安全地验证你的证书、如何强制全站走 HTTPS、以及通过响应头把常见的客户端攻击面压到最小。学完你就能像一台生产网站那样把 Nginx 的"安全头"配齐。

为什么证书配好了还不够

证书解决了"加密与身份",但还有几个问题:

  • 吊销状态检查:证书可能被吊销(密钥泄露等),浏览器需要确认它是否仍有效。这就是 OCSP / CRL 的作用。
  • 降级攻击:用户访问 http:// 时可能被中间人劫持或降级成明文传输。需要 HSTS 强制浏览器永远用 HTTPS。
  • 客户端攻击:XSS、点击劫持、MIME 嗅探、中间人注入第三方脚本——都需要响应头来防御。

OCSP Stapling(证书状态缓存)

在线证书状态协议(OCSP)是浏览器向 CA 查询证书是否被吊销。每次连接都查会很慢,而且泄露访问习惯。OCSP Stapling 由服务器定时向 CA 拉取一个"签名的状态快照",并在 TLS 握手里主动发给浏览器,又快又省。

Nginx 里开启:

ssl_stapling on;
ssl_stapling_verify on;
resolver 127.0.0.53 valid=30s;   # 用本机 DNS 解析 CA 地址,别用已停服的 8.8.8.8

验证是否生效:

# 检查握手是否携带 stapled response
openssl s_client -connect learn.bangdream.xyz:443 -status -servername learn.bangdream.xyz 2>/dev/null | grep -A2 'OCSP response:'
# 应看到 OCSP Response Status: successful (0x0)

HSTS(HTTP Strict Transport Security)

告诉浏览器:"此域接下来 一定 用 HTTPS,别允许降级到 http"。加了它,以后即使你忘了输 https://,浏览器也会自动用 HTTPS;而且能防范 SSL stripping 攻击。

# 在 443 的 server block 里加:max-age 指缓存秒数,includeSubDomains 覆盖子域,preload 申请进浏览器预加载名单
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

注意 | 一旦开 HSTS,浏览器就不会再允许 http 访问。务必确认你的整站(含所有子域、资源)都已支持 HTTPS 再启用 includeSubDomains;特别是还没配证书的子域会被直接判死。建议先只开 max-age 观察一段时间,再加 includeSubDomains。

关键安全响应头一览

响应头 作用 推荐值
Strict-Transport-Security 强制 HTTPS max-age=31536000; includeSubDomains
X-Content-Type-Options 禁止 MIME 嗅探,防类型混淆攻击 nosniff
X-Frame-Options 禁止页面被 iframe 嵌套,防点击劫持 SAMEORIGIN 或 DENY
X-XSS-Protection 旧版 XSS 过滤(现代浏览器已废弃,保留无害) 1; mode=block
Referrer-Policy 控制向第三方泄露哪些来源信息 strict-origin-when-cross-origin
Content-Security-Policy 白名单允许加载的脚本/样式/图片,防 XSS 注入 按站点收紧
Permissions-Policy 禁掉用不到的权限(摄像头/麦克风) camera=(), microphone=()

一个"安全头套餐"配置示例(放在 server 块,always 保证错误响应也带上):

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

注意 | add_header 在 location 里会覆盖上层 server 的同名字段。如果你在某个 location 里也写了 add_header,那 server 级别的全部安全头都会失效。最常见坑:在 location / 放了 proxy_pass 又加了自己的 add_header,导致根级安全头全丢。解决:要么统一放在一个位置,要么把安全头抽成 include 片段。

检查结果

用 curl 看你的站点实际返回了哪些头:

curl -sSI https://learn.bangdream.xyz/ | grep -iE "strict-transport|x-content-type|x-frame|referrer|content-security"

延伸:Content-Security-Policy 实战

CSP 是"白名单",只有白名单内的来源才允许执行。一个站点想用 CDN 引脚本、内联样式,可这样配:

add_header Content-Security-Policy "default-src 'self'; img-src 'self' https:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'" always;

提示 | CSP 配太严会造成白屏,配太松等于没配。先用 Content-Security-Policy-Report-Only 跑一段线上观察违规,再切正式策略。

相关命令

  • 查看证书的 OCSP 响应与证书链: openssl s_client -connect <域名>:443 -status -showcerts
  • 一键安全体检: curl -sSI https://<域名>/ 人工核对头;或用在线工具(如 Mozilla Observatory)扫描。
  • 禁止子域 URL 泄露: add_header Referrer-Policy "no-referrer"(对隐私敏感站点)。