使用 CSP(内容安全策略)防范 XSS
什么是 CSP
CSP(Content Security Policy)是一种由浏览器实现的安全机制,用于检测和缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。它通过定义允许加载资源的来源,限制浏览器只能执行或渲染来自这些来源的资源。CSP 的核心思想是白名单机制,开发者明确告诉浏览器哪些资源可以被加载,其他资源一律禁止。
CSP 如何防范 XSS
XSS 攻击通常通过注入恶意脚本到网页中执行。CSP 通过以下方式有效防范 XSS:
- 禁止内联脚本执行:默认情况下,CSP 会阻止
<script>
标签中的内联代码和javascript:
伪协议 - 限制脚本来源:只允许从特定域加载脚本文件
- 阻止 eval 类函数:禁用
eval()
、setTimeout(string)
等动态代码执行方式 - 限制非静态资源:控制字体、框架、图片等资源的加载源
实现 CSP 的三种方式
通过 HTTP 头实现
最推荐的方式是通过 HTTP 响应头设置:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src *; media-src media1.com media2.com; report-uri /csp-violation-report-endpoint
通过 meta 标签实现
适用于无法修改服务器配置的情况:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
通过报告模式实现
先监控不强制执行,适合调试阶段:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violation-report-endpoint
CSP 指令详解
常用指令
default-src
: 默认策略,适用于未单独指定的指令script-src
: 控制 JavaScript 的加载源style-src
: 控制 CSS 的加载源img-src
: 控制图片的加载源connect-src
: 控制 XHR、WebSocket 等连接的源font-src
: 控制字体文件的加载源object-src
: 控制<object>
、<embed>
、<applet>
等标签media-src
: 控制<audio>
、<video>
等媒体资源frame-src
: 控制<iframe>
的加载源(已废弃,改用 child-src)child-src
: 控制子上下文(iframe、worker 等)worker-src
: 控制 worker、shared worker、service worker 的源
特殊值
'none'
: 禁止任何资源'self'
: 只允许同源资源'unsafe-inline'
: 允许内联脚本/样式(降低安全性)'unsafe-eval'
: 允许动态代码执行(如 eval)'strict-dynamic'
: 信任由已执行脚本动态加载的脚本'nonce-<base64值>'
: 通过一次性随机数允许特定内联脚本'sha256-<hash值>'
: 通过哈希值允许特定内联脚本
CSP 实战配置示例
基础安全配置
Content-Security-Policy:
default-src 'none';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
form-action 'self';
frame-ancestors 'none';
base-uri 'self';
report-uri /csp-report
严格配置(推荐)
Content-Security-Policy:
default-src 'none';
script-src 'self' https://cdn.example.com;
style-src 'self';
img-src 'self' data: https://*.example-cdn.com;
font-src 'self';
connect-src 'self' https://api.example.com;
form-action 'self';
frame-ancestors 'none';
base-uri 'self';
report-uri /csp-report
允许 Google Analytics 的配置
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.google-analytics.com 'unsafe-inline';
img-src 'self' https://www.google-analytics.com;
style-src 'self' 'unsafe-inline';
connect-src 'self' https://www.google-analytics.com;
report-uri /csp-report
处理内联脚本和样式
使用 nonce
服务器生成随机数并注入 CSP 头和脚本标签:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// 这个脚本会被执行
</script>
<script>
// 这个脚本会被阻止
</script>
使用哈希
计算脚本内容的 SHA 哈希并添加到 CSP:
Content-Security-Policy: script-src 'sha256-abc123...'
<script>
// 内容必须完全匹配哈希计算时的内容
alert('Hello, world.');
</script>
CSP 报告机制
违规报告配置
Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint
报告示例(POST 数据)
{
"csp-report": {
"document-uri": "https://example.com/page.html",
"referrer": "https://example.com/",
"violated-directive": "style-src 'self'",
"effective-directive": "style-src",
"original-policy": "default-src 'self'; style-src 'self'",
"disposition": "enforce",
"blocked-uri": "https://evil.com/malicious.css",
"line-number": 10,
"column-number": 20,
"source-file": "https://example.com/page.html",
"status-code": 200,
"script-sample": ".evil { color: red; }"
}
}
常见问题与解决方案
第三方集成问题
问题:使用第三方库(如 jQuery、Bootstrap)时 CSP 报错
解决方案:
- 将库托管到自己的服务器并使用
'self'
- 添加 CDN 地址到白名单:
script-src 'self' https://code.jquery.com
- 使用子资源完整性(SRI):
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha384-KyZXEAg3QhqLMpG8r+Knujsl5/1EN..." crossorigin="anonymous"></script>
内联事件处理器问题
问题:onclick
等 HTML 事件处理器被阻止
解决方案:
- 改用外部脚本添加事件监听器
document.getElementById('myButton').addEventListener('click', function() { // 处理点击 });
- 使用
'unsafe-inline'
(不推荐) - 使用 CSP Level 3 的
'unsafe-hashes'
动态样式问题
问题:JavaScript 动态修改样式被阻止
解决方案:
- 改用 class 切换而非直接修改 style
element.classList.add('active');
- 使用
'unsafe-inline'
或哈希/nonce
CSP 与现代前端框架
React 应用配置
React 需要 'unsafe-inline'
用于其事件系统:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self' https://api.example.com;
Vue 应用配置
Vue 需要 'unsafe-eval'
用于模板编译:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
Angular 应用配置
Angular 需要 'unsafe-eval'
用于 JIT 编译:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
对于 AOT 编译的 Angular 应用,可以移除 'unsafe-eval'
。
CSP 部署策略
分阶段部署
- 报告阶段:使用
Content-Security-Policy-Report-Only
监控潜在问题 - 宽松策略:初始部署时允许必要的
'unsafe-inline'
和'unsafe-eval'
- 严格策略:逐步移除不安全选项,使用 nonce/hash 替代
- 持续优化:根据报告不断调整策略
监控与维护
- 设置报告端点收集违规信息
- 定期审查 CSP 报告
- 随着应用更新调整 CSP 策略
- 使用自动化工具测试 CSP 有效性
CSP 高级技巧
使用 strict-dynamic
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'
允许由已信任脚本动态加载的脚本执行,适用于现代模块化应用。
多个 CSP 策略
可以组合多个 CSP 头,策略会叠加限制:
Content-Security-Policy: script-src 'self'
Content-Security-Policy: script-src https://cdn.example.com
最终策略是 script-src 'self' https://cdn.example.com
预加载指令
Content-Security-Policy:
default-src 'self';
script-src 'self';
preload-src 'self' https://cdn.example.com
控制 <link rel="preload">
资源的来源。
CSP 与其他安全机制的协同
与 Subresource Integrity (SRI) 结合
<script src="https://example.com/script.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
与 Feature Policy 结合
Content-Security-Policy: default-src 'self'
Feature-Policy: geolocation 'none'; microphone 'none'
与 Expect-CT 结合
Content-Security-Policy: default-src 'self'
Expect-CT: max-age=86400, enforce, report-uri="https://example.com/report"
CSP 的局限性
- 浏览器兼容性:不同浏览器对 CSP 的支持程度不同
- 配置复杂性:严格的 CSP 可能需要重构现有代码
- 误报问题:过于严格的策略可能阻止合法资源
- 性能影响:复杂的策略可能增加浏览器处理时间
- 不防所有攻击:不能防范 CSRF、SQL 注入等其他攻击
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn