CSP 内容安全策略是什么?它能防什么?
简化版
CSP 是浏览器提供的内容安全策略,通过 HTTP 响应头限制页面可以加载哪些脚本、样式、图片、字体等资源。它能降低 XSS、数据注入和恶意资源加载风险,但不能替代输入校验和输出转义。
详细版
CSP 常通过响应头配置:
Content-Security-Policy: default-src 'self'; script-src 'self'
常见指令:
default-src:默认资源来源。script-src:脚本来源。style-src:样式来源。img-src:图片来源。connect-src:接口请求来源。frame-ancestors:限制谁能嵌入当前页面。
CSP 可以禁止内联脚本、限制第三方脚本来源、上报违规行为。它是重要防线,但配置过严可能影响业务资源加载。
完整版教学
一、CSP 的核心思想
XSS 的危险在于攻击者让页面执行了不该执行的脚本。CSP 的思路是提前告诉浏览器:“只有这些来源的脚本能执行,其它都不行。”
这样即使页面出现了注入点,攻击脚本也可能因为违反策略而无法执行。
二、常见配置
最基础的配置是只允许同源资源:
Content-Security-Policy: default-src 'self'
如果业务需要加载 CDN 脚本,就要显式加入可信域名。
对于脚本安全,可以减少或禁止 'unsafe-inline'。如果确实需要内联脚本,可以考虑 nonce 或 hash 机制。
三、CSP 的边界
CSP 不能修复所有安全问题。它不能替代服务端鉴权,也不能替代富文本过滤。
如果策略配置过松,比如允许所有脚本来源,效果会很弱。配置过严又可能导致页面资源加载失败。因此它需要结合业务资源清单逐步上线。
四、面试追问与工程落地
面试官可能问:“CSP 如何灰度上线?”
可以先使用 Content-Security-Policy-Report-Only,只上报违规但不拦截资源。观察一段时间后修正规则,再切换到正式拦截模式。
工程中要把第三方脚本纳入治理,避免任何业务都随意加外链脚本。
五、nonce、hash 与严格策略的机制
仅按域名列白名单可能仍信任被攻陷的第三方脚本。严格 CSP 更常围绕每次响应生成的 nonce 或静态脚本 hash 建立执行授权:
Content-Security-Policy: script-src 'nonce-r4Nd0mValue' 'strict-dynamic'; object-src 'none'; base-uri 'none'
| 方式 | 适用场景 | 关键约束 |
|---|---|---|
| nonce | SSR 动态 HTML | 每个响应重新生成,值不可预测 |
| hash | 固定内联脚本 | 脚本文本变化就要更新 hash |
| 主机白名单 | 迁移期资源治理 | 受信域被攻陷时风险仍在 |
'unsafe-inline' | 兼容旧代码 | 会显著削弱脚本注入防护 |
nonce 必须由服务端使用足够随机性生成,不能写死或按用户可预测信息计算。给可信入口脚本 nonce 后,strict-dynamic 可让支持它的浏览器信任该脚本动态加载的后续脚本,减少庞大域名名单。
CSP 是浏览器执行层的纵深防线:根治注入仍靠上下文编码和安全 DOM API,CSP 用来限制漏网内容能做什么。
六、灰度、上报和数字化验收
存量站点可先运行 7 天 Content-Security-Policy-Report-Only,统计违反指令、资源地址和页面版本。若每天 100 万次访问产生 5 万条同一扩展插件报告,应在采集端聚合采样,而不是直接放宽策略。
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'nonce-...'; report-to csp-endpoint
验收不只是“页面能打开”:尝试插入无 nonce 的内联脚本应被拦截;合法 nonce 脚本应执行;图片、接口、frame 分别受 img-src、connect-src、frame-src 控制;页面是否能被嵌入则看 frame-ancestors。
报告可能含页面地址等敏感信息,采集端要做权限、保留周期和脱敏。切换正式策略时分页面灰度,并保留回滚能力,避免一次配置错误造成全站白屏。
七、常见误区与追问
- 误区:配置 CSP 就彻底修复了 XSS。 CSP 是纵深防线,危险 sink 和错误编码仍需从代码中修复。
- 误区:
default-src 'self'会覆盖所有 CSP 指令。frame-ancestors等指令没有该回退,必须单独声明。 - 误区:nonce 可以写成固定常量复用。 可预测或复用的 nonce 可能被攻击内容利用,应逐响应随机生成。
- 追问:为什么尽量不使用
'unsafe-inline'? 它允许大量内联脚本执行,会重新打开常见注入路径。 - 追问:hash 与 nonce 怎么选? 固定脚本适合 hash,服务端动态页面通常更适合逐响应 nonce。
- 追问:Report-Only 会拦截资源吗? 不会,它只报告违规,适合盘点存量依赖和灰度。
- 追问:CSP 能限制数据外传吗?
connect-src、img-src、form-action等可缩小通道,但不能替代敏感数据最小化。
八、加强记忆
- 先治理:普通文本编码、富文本清洗、安全 DOM API 是根本。
- 再约束:CSP 限制脚本、连接、frame 等资源能力。
- 严格脚本:动态页用逐响应 nonce,固定内容可用 hash。
- 拒绝放水:谨慎使用
unsafe-inline、通配域和过宽 data 源。 - 灰度上线:Report-Only 盘点,采样聚合,再分批强制。
- 持续验证:合法资源可用、恶意内联被拦、报告可观测且可回滚。