Blog
给博客加上安全头与 CSP 的务实做法
博客是静态站,没有用户登录,仍值得认真对待响应头。不是因为「静态站天天被黑成肉鸡」,而是头的边际成本低,能收紧一类 XSS 与错误 MIME 的伤害面,也让扫描报告不那么刺眼。静态站的 equities 不高,但被嵌套钓鱼、被错误类型执行、或把文章路径泄漏给一堆外链,仍然是可以避免的尴尬。下面不按「安全清单打勾」写,而是把我实际启用的响应头逐项讲:它防什么、静态站场景下取值怎么选、和 Cloudflare 叠加时要注意谁说了算。
Strict-Transport-Security
Strict-Transport-Security: max-age=31536000; includeSubDomains
作用:告诉浏览器在 max-age 秒内只通过 HTTPS 访问该主机(及可选子域)。确认全站证书与跳转无误后再上长期 max-age;过早打开等于把 HTTP 调试窗口焊死。预加载列表(preload)更激进,家庭博客通常不必第一天就冲。HSTS 管的是传输通道强制,不替代 CSP,也不处理内容注入本身。
CDN 若已在边缘加 HSTS,源站再加一份一般可接受,但注意重复与策略是否一致,避免子域尚未全 HTTPS 却写了 includeSubDomains。
X-Content-Type-Options
X-Content-Type-Options: nosniff
作用:阻止浏览器 MIME 嗅探,降低「服务器当文本返回、浏览器当脚本执行」的经典坑。静态博客几乎没有理由关掉。若某个老旧资源依赖嗅探才能工作,应修资源的 Content-Type,而不是删这个头。和 CSP 互补:一个管类型信任,一个管加载来源。
Referrer-Policy
Referrer-Policy: strict-origin-when-cross-origin
作用:控制跳转到外站时携带多少 Referer。strict-origin-when-cross-origin 是常见折中:同站保留路径,跨站 HTTPS→HTTPS 只留 origin,降级场景更少泄漏。博客若嵌入统计或外链很多,过宽的 Referer 会把文章路径送给第三方。需要更狠可用 no-referrer,但部分统计与外链预览会变哑。
Permissions-Policy
Permissions-Policy: camera=(), microphone=(), geolocation=()
作用:显式禁用浏览器强能力 API。静态站本身不该申请摄像头与麦克风;写上是为了防止被嵌入的第三方脚本或未来误加的小组件悄悄调用。按需增删:若某页真有地图定位,再给该 origin 开口,而不是全局放行。旧名 Feature-Policy 已过时,新站写 Permissions-Policy。
点击劫持相关:X-Frame-Options 与 frame-ancestors
X-Frame-Options: DENY
# 或在 CSP 中:
# frame-ancestors 'none'
作用:防止页面被嵌进别人的 iframe 做 UI 伪装。现代更推荐 CSP 的 frame-ancestors;X-Frame-Options 仍有广泛支持,可并存但语义不要互相打架。博客若需要被可信域名嵌入(少数文档站场景),改为白名单而不是直接删防护。
Content-Security-Policy:先报告,再执法
CSP 是这一组里最容易误伤也最值得做的项。它声明浏览器允许从哪些源加载脚本、样式、图片、字体、连接。静态博客可以较严,但仍要为 CDN、字体、封面图、可能的统计域名留白名单。Hexo 一类生成器若把高亮或评论脚本以内联方式塞进 HTML,第一次上强制 CSP 时控制台会红成一片——这是预期,不是站点坏了。
务实路径:
- 先发
Content-Security-Policy-Report-Only,观察一周 - 看浏览器控制台与 report 端点(若有)
- 收紧后改为强制
Content-Security-Policy
示例起点(按站点改域名):
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https:; \
style-src 'self' 'unsafe-inline'; script-src 'self'; \
base-uri 'self'; frame-ancestors 'none'
script-src 尽量少 'unsafe-inline';生成器支持 nonce 或把脚本外置时更干净。主题与统计脚本属于供应链:CSP 能限制损害半径,不是银弹。依赖尽量少,能上 SRI 的第三方脚本就上。img-src 若写得过死,外链配图与 emoji CDN 会裂图,报告模式阶段把真实引用域记进白名单再执法,比一上来 default-src 'none' 更不伤读者。
叠加 CDN 时如何验收
头可以加在 OpenResty、Nginx 或 Cloudflare Transform Rules。只在一处做「权威源」,另一处不要静默覆盖成冲突值。验收:
curl -sI https://blog.example.com | grep -iE \
'strict-transport|content-security|x-content-type|referrer-policy|permissions-policy|x-frame'
securityheaders.com 可作参考,分数不是 KPI,别为了 A+ 拆掉必要功能。改主题或加统计后做一次回归:CSP 最常在那时炸。本地用无痕窗口打开首页与一篇含代码高亮、外链图片的文章,看控制台是否被策略拦截;比只 curl 响应头更能发现「头在、页面挂」的不一致。
若博客同时走 Cloudflare 与源站,改完头后分别对「绕过缓存的 URL」与「命中缓存的 URL」测一次,确认 Transform Rules 与源站头没有一个旧一个新。安全头是卫生习惯;博客这类 equities 不高的资产,做完可以暂时忘掉,直到下一次改前端再逐项核对——逐项,而不是只看一个总分。