安全头
设锁定浏览器的 HTTP 响应头: CSP、HSTS、X-Frame-Options 等。
为什么
安全头是浏览器侧控制,几行配置防整类攻击。Content-Security-Policy 通过限制脚本加载源止 XSS——即使攻击者注入 <script> 标签浏览器也拒执行。HSTS 强制 HTTPS 防 SSL 剥离降级。X-Frame-Options 通过禁你页被框防点击劫持。这些头免费,在边缘(Nginx、Cloudflare 或框架中间件)设一次,保护每个响应无需任何代码改。它们是最高 ROI 安全控制: 几小时配置防代码中要数周修的攻击。
何时使用
首次生产部署前设安全头并在每次 staging 部署验。新功能加外部资源(第三方脚本、CDN 字体、iframe 嵌入)时审。
如何做
1. 设 Content-Security-Policy(CSP)——最重要的头 - 先 report-only 模式: Content-Security-Policy-Report-Only - 收违规报告,调策略直到无合法流量被阻 - 然后强制: script-src 'self' + 特定可信源;无 'unsafe-inline' 2. 设传输和框头 - Strict-Transport-Security: max-age=63072000; includeSubDomains; preload - X-Frame-Options: DENY(或 CSP frame-ancestors 'none'——现代等价) - X-Content-Type-Options: nosniff(防 MIME 嗅探) 3. 设隐私和引用头 - Referrer-Policy: strict-origin-when-cross-origin(少泄露给第三方) - Permissions-Policy: camera=(), microphone=(), geolocation=()(禁未用 API) - Cross-Origin-Opener-Policy: same-origin(隔离浏览上下文) 4. 在边缘或框架中间件配 - Nginx: server 块中 add_header - Cloudflare: Transform Rules 或 Workers - Next.js: next.config.js headers() 或 middleware - Helmet(Express): app.use(helmet()) 设合理默认 5. 用扫描器和头检查工具验 - securityheaders.com 或 Mozilla Observatory——目标 A+ 级 - ZAP 被动扫标缺/错配头 - 测 CSP 不破合法功能(第三方脚本、内联样式)
示例
# Nginx: /etc/nginx/sites-enabled/app.conf
server {
listen 443 ssl http2;
server_name example.com;
add_header Content-Security-Policy
"default-src 'self'; \
script-src 'self' https://plausible.io; \
style-src 'self' 'unsafe-inline'; \
img-src 'self' data: https:; \
connect-src 'self' https://api.example.com; \
frame-ancestors 'none'; \
base-uri 'self'; \
form-action 'self'" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
# Next.js: next.config.js
const cspHeader = [
"default-src 'self'",
"script-src 'self' https://plausible.io",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"frame-ancestors 'none'",
].join("; ");
module.exports = {
async headers() {
return [{
source: "/(.*)",
headers: [
{ key: "Content-Security-Policy", value: cspHeader },
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Content-Type-Options", value: "nosniff" },
],
}];
},
};常见问题
- ⚠CSP script-src 设 'unsafe-inline' 避破内联脚本——这禁了 CSP 本该提供的 XSS 保护,不如不设头。
- ⚠HSTS 用短 max-age(如 3600 = 1 小时)——浏览器一小时后忘策略又易受 SSL 剥离;用 63072000(2 年)含 preload。
- ⚠错误响应(404、500)忘设头——Nginx add_header 无 always 仅 2xx 设,错误页无 CSP 可利用。
- ⚠应用永不被合法框时设 X-Frame-Options: SAMEORIGIN——DENY 更严禁所有框,若你完全不用框则对。
最佳实践
- ✓CSP 先 Report-Only 模式,收一周违规报告,调策略直到无合法流量触发违规——然后强制,不破真实用户。
- ✓用 'always' 标志(Nginx)或等价使错误响应也设头——无 CSP 的错误页是攻击面,大多错配在非 2xx 响应。
- ✓用 CSP frame-ancestors 'none' 替 X-Frame-Options(现代等价覆盖所有框非仅同源),两者都设为与旧浏览器纵深防御。
- ✓每次部署后用 securityheaders.com 或 Mozilla Observatory 验头——被设后静默移除的头是常见回归。