安全响应头与 CSP:给站点加一层护栏
HTTPS 解决的是「传输过程不被偷看」,安全响应头解决的是「浏览器拿到内容之后别乱来」。几行配置,成本极低。
一、四个基础头,加上就有效
# MIME 嗅探防护:别让浏览器把 .txt 当 .js 执行
add_header X-Content-Type-Options "nosniff" always;
# 点击劫持防护:不允许被别的站点 iframe 嵌套
add_header X-Frame-Options "SAMEORIGIN" always;
# 跨站请求时带多少来源信息
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 浏览器危险 API 权限(本站不用就全关)
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;X-Frame-Options 的三个值
| 值 | 效果 |
|---|---|
DENY | 完全禁止被嵌套 |
SAMEORIGIN | 只允许同源页面嵌套(推荐) |
ALLOW-FROM uri | 允许指定来源(已废弃,用 CSP 的 frame-ancestors 代替) |
Referrer-Policy 怎么选
| 值 | 跨站时发送 |
|---|---|
no-referrer | 什么都不发 |
strict-origin-when-cross-origin | 只发域名,且 HTTPS→HTTP 时不发(推荐) |
unsafe-url | 发完整 URL(含路径参数,有泄露风险) |
二、HSTS:强制走 HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;作用是告诉浏览器「接下来一年,这个域名只许用 HTTPS 访问」。能防住首次访问的降级攻击(SSL Strip)。
但它有反噬:加上之后,如果你证书出问题,用户连「忽略警告继续访问」的选项都没有。所以稳妥做法是逐步加:
# 第一步:先用短时长试跑,确认全站 HTTPS 都正常
add_header Strict-Transport-Security "max-age=300" always;
# 第二步:稳定一周后加长
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 第三步(可选):申请加入浏览器内置 HSTS 预载列表
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
preload一旦提交到 Chrome 的预载列表,撤回极慢(几个月)。没把握别加。
三、CSP:内容安全策略
CSP 是最强也最容易把站点搞挂的一个。它告诉浏览器「只允许加载这些来源的资源」,能有效挡住 XSS 注入的外链脚本。
先给一条适合纯静态站的严格配置:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'" always;逐条解释:
| 指令 | 作用 |
|---|---|
default-src | 所有资源的默认策略 |
script-src | 允许哪些地方加载 JS |
style-src | 允许哪些地方加载 CSS |
img-src | 图片来源,data: 是内联 base64 |
connect-src | fetch / XHR / WebSocket 能连哪 |
frame-ancestors | 谁可以 iframe 嵌套我(替代 X-Frame-Options) |
base-uri | 限制 <base> 标签,防相对路径劫持 |
form-action | 表单能提交到哪 |
常见来源值:
| 值 | 含义 |
|---|---|
'self' | 同源 |
'none' | 完全禁止 |
'unsafe-inline' | 允许内联 script/style(会降低安全性) |
'unsafe-eval' | 允许 eval(尽量避免) |
https://cdn.x.com | 指定域名 |
data: / blob: | 协议白名单 |
四、CSP 从零上线的稳妥步骤
直接上严格策略,很可能把站点样式或脚本全挡了。正确做法是先观察:
第一步:用 Report-Only 模式跑一周
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri /csp-report" always;这个头只报告不拦截。打开浏览器控制台,被拦的资源会以警告形式列出,但页面照常工作。
第二步:收集报告,把合法来源加进白名单
如果用了 Google Fonts、CDN 上的 JS、内联脚本,逐一补进去:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:;" always;第三步:正式启用,去掉 Report-Only
五、内联脚本怎么办
严格 CSP 会禁止 onclick="..." 和 <script>...</script> 内联写法。三种解法,按推荐度排序:
- 改成外部文件(最干净);
- 用 nonce:服务器每次生成一个随机值,只有带该值的脚本能执行;
- 用 hash:把脚本内容算 sha256 写进策略(内容一变就失效,适合固定片段)。
# nonce 方案:Nginx 用变量生成,每次请求都不同
add_header Content-Security-Policy "script-src 'self' 'nonce-$request_id'" always;<script nonce="这里的值要由服务端模板输出">…</script>纯静态站没有服务端渲染,最简单的办法就是:把内联脚本都挪到外部 JS 文件里。本站就是这么做的——所有 JS 在 assets/js/ 下,CSP 可以直接设得很严。
六、一份整合配置
直接粘到 server 块里:
server {
listen 443 ssl http2;
server_name www.bzii.cn;
# 安全头
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 Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# CSP(纯静态站版本)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'self'; base-uri 'self'" always;
root /var/www/baize;
index index.html;
location / { try_files $uri $uri/ /index.html; }
}
always参数很重要:不加的话,Nginx 只对 200/201/204/206/301/302/303/304/307/308 这些响应加头,4xx/5xx 响应就不会带。
七、验证
# 看响应头
curl -sI https://www.bzii.cn | grep -iE "content-security|x-frame|x-content|strict-transport|referrer"在线工具推荐:
- securityheaders.com —— 打分并解释每一项;
- Mozilla Observatory —— 更严格,会给出具体改进建议。
拿到 A 或 A+ 并不难,基本就是上面这几行的事。
