一、内容安全策略(CSP)是什么
简单来说,CSP就像是给网站加了个保安队长,专门盯着哪些资源能加载、哪些行为被允许。想象一下,你开了一家店,CSP就是门口的安检仪,只有符合规定的货物才能进店。
这个保安队长通过HTTP响应头来工作,最常见的配置方式是这样的:
<!-- 示例1:基础CSP配置 -->
<!-- 技术栈:HTML/HTTP -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://trusted.cdn.com">
这个配置的意思是:
default-src 'self':默认只允许加载同源的资源script-src额外允许加载来自trusted.cdn.com的脚本
二、为什么要用CSP
去年我帮一个电商网站做安全审计,发现他们的商品详情页被注入了挖矿脚本。攻击者利用评论区XSS漏洞,让每个访问页面的用户电脑都变成矿机。如果当时有CSP防护,这种攻击根本不会得逞。
CSP主要防范这些攻击:
- XSS攻击(各种类型的跨站脚本)
- 点击劫持
- 混合内容攻击
- 数据注入攻击
来看个实际案例:
<!-- 示例2:防御XSS的CSP配置 -->
<!-- 技术栈:HTML/HTTP -->
Content-Security-Policy:
default-src 'none';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' fonts.googleapis.com;
img-src 'self' data:;
font-src 'self' fonts.gstatic.com;
connect-src 'self' api.example.com;
frame-ancestors 'none';
这个配置:
- 默认禁止所有资源加载
- 只允许特定类型的资源从指定来源加载
- 完全禁止iframe嵌套(防点击劫持)
三、CSP配置详解
3.1 常用指令说明
我整理了个配置速查表:
<!-- 示例3:完整的CSP指令示例 -->
<!-- 技术栈:HTML/HTTP -->
Content-Security-Policy:
default-src 'self'; # 默认策略
script-src 'self' # 脚本
'sha256-abc123...' # 允许特定哈希值的脚本
'nonce-random123'; # 允许带特定nonce的脚本
style-src 'self' 'unsafe-inline'; # 样式
img-src *; # 图片允许任何来源
media-src media1.com media2.com; # 媒体文件
frame-src 'self'; # iframe来源
font-src fonts.com; # 字体文件
connect-src 'self' # 连接限制
api.example.com;
report-uri /csp-report; # 违规报告地址
3.2 特殊值的使用
新手常问:"'unsafe-inline'是不是很危险?" 确实,但有时不得不使用:
<!-- 示例4:必须使用内联脚本的场景 -->
<!-- 技术栈:HTML/HTTP -->
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// 必须内联执行的初始化代码
initApp();
</script>
<!-- CSP需要这样配置 -->
Content-Security-Policy:
script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
这种情况下:
- 为内联脚本添加随机nonce值
- CSP只允许带有正确nonce的脚本执行
- 比直接使用'unsafe-inline'安全得多
四、实战配置建议
4.1 渐进式部署策略
我建议分三个阶段部署CSP:
<!-- 示例5:分阶段部署的CSP -->
<!-- 技术栈:HTML/HTTP -->
<!-- 第一阶段:仅报告模式 -->
Content-Security-Policy-Report-Only:
default-src 'self';
report-uri /csp-report;
<!-- 第二阶段:限制性策略+报告 -->
Content-Security-Policy:
default-src 'self';
script-src 'self';
report-uri /csp-report;
<!-- 第三阶段:严格策略 -->
Content-Security-Policy:
default-src 'none';
script-src 'self' 'nonce-...';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
4.2 常见问题解决方案
问题1:第三方小工具加载失败
<!-- 示例6:第三方资源处理方案 -->
<!-- 技术栈:HTML/HTTP -->
Content-Security-Policy:
script-src 'self'
https://apis.google.com
https://connect.facebook.net;
frame-src
https://youtube.com
https://facebook.com;
问题2:WebSocket连接被阻止
<!-- 示例7:WebSocket处理方案 -->
<!-- 技术栈:HTML/HTTP -->
Content-Security-Policy:
connect-src 'self'
wss://api.example.com
https://push.example.com;
五、高级防护技巧
5.1 哈希和Nonce的应用
<!-- 示例8:哈希值使用示例 -->
<!-- 技术栈:HTML/HTTP -->
<script>
// 这个脚本的SHA256哈希是:qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=
function loadAnalytics() {
// 关键初始化代码
}
</script>
<!-- CSP配置 -->
Content-Security-Policy:
script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=';
5.2 严格动态特性
<!-- 示例9:strict-dynamic用法 -->
<!-- 技术栈:HTML/HTTP -->
Content-Security-Policy:
script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa' 'strict-dynamic';
这个配置:
- 信任带有正确nonce的脚本
- 允许这些脚本动态加载其他脚本
- 向后兼容性更好
六、注意事项与总结
6.1 常见陷阱
- 不要过度依赖'unsafe-inline'
- 避免使用'unsafe-eval'除非绝对必要
- 记得为所有资源类型设置策略
- 测试所有用户流程
6.2 最佳实践建议
根据我的经验,推荐这些配置原则:
- 从default-src 'none'开始
- 逐步添加必要的资源类型
- 使用nonce而不是unsafe-inline
- 实施报告机制
- 定期审查CSP报告
6.3 性能考量
虽然CSP会增加一些开发成本,但安全收益远大于开销。一个好的CSP策略:
- 平均增加约50ms的页面加载时间
- 但能阻止90%以上的XSS攻击
- 减少混合内容问题
最后提醒:CSP不是银弹,需要与其他安全措施(如输入验证、输出编码)配合使用才能发挥最大效果。
评论