如何用 CSP 保护自己的网站
2024-10-08
Web 世界很精彩,同时也很危险。
最近公司一直在推零信任网络 (Zero Trust),尽管这通常是针对内网环境,但在公网环境下,安全同样重要。
根据维基百科中 2010 年的 报告🔗,Web 安全威胁前十名中,XSS (Cross-site scripting,跨站脚本攻击) 排名第二,仅次于代码注入 (Injection)。
当前这个网站,是通过设置 HTTP 头字段中的 Content-Security-Policy
和 <meta>
字段减少 XSS。
1. CSP 含义
XSS 攻击,利用了浏览器对于服务器所获取内容的信任。
如果获取到的是恶意脚本,在本地执行就会很危险,因为浏览器误认为这是一段正常程序。
CSP 就是通过限制可执行脚本的来源,从而减少 XSS 攻击的可能性。相当于指定一份白名单,即便恶意代码被注入,因为来源不在白名单中,会被拒绝执行。
CSP 的全称是 Content-Security-Policy,即:内容安全策略。
使用的方式有两种。
一种是配置服务器,返回 Content-Security-Policy
🔗 HTTP 标头。
另一种是配置 <meta>
字段,类似:
2. CSP 使用
CSP 的语法遵循下列格式:
具体的 policy 可通过 指令 选项值
构建。
指令主要有 5 种。获取指令,文档指令,导航指令,报告指令和其他指令等。具体可参照:Content-Security-Policy - HTTP | MDN🔗。
常用的有以下这些:
- child-src:为 Web Workers 和其他内嵌内容,例如
<iframe>
加载的内容定义合法源 - connect-src:指定可以通过脚本加载的 URL
- default-src:为其他指令提供备用服务,可理解为兜底
- font-src:设置允许
@font-face
加载的字体源 - frame-src:指定允许
<iframe>
加载的源 - img-src:限制加载图片的源
- manifest-src:限制应用声明文件的源
- media-src:限制通过
<audio>
,<video>
或<track>
标签加载的媒体文件的源 - object-src:限制
<object>
,<embed>
标签的源 - prefetch-src:指定预加载允许的源
- script-src:限制 JavaScript 加载的源
- style-src:限制 CSS 文件加载的源
- worker-src:限制 Worker、SharedWorker 或 ServiceWorker 脚本源
- block-all-mixed-content:当使用 HTTPS 时阻止使用 HTTP 加载任何资源
- upgrade-insecure-requests:让浏览器把所有 HTTP 访问替换为 HTTPS 访问
选项值通常是由下列格式构成:
<协议>://<主机>/<路径>
可以使用通配符 * 进行模糊匹配,选项值可以有多个。
另外单独有两个关键词,'self'
和 'none'
,分别表示当前域名和禁止加载外部资源,各自都需要加引号。
例如:
单独针对 script-src,还有 5 个特殊值。
-
unsafe-inline:允许内联脚本和样式,即下列形式:
但是,允许内联脚本执行,通常被认为有安全风险。因此建议使用 nonce 值 和 hash 值代替。
-
nonce 值:使用加密安全的随机令牌生成器生成一个随机 nonce 值,并包含在策略中,因为是动态生成的,因此每次的 HTTP 请求返回结果都不同
同时将生成的 nonce 值放置在内联脚本中。
-
hash 值:也可根据内联脚本生成 hash 值,CSP 支持 sha256, sha384 和 sha512
sha256 的值生成自脚本(不包含 script):
-
-
unsafe-hashes:按照上述办法,event handler 依然不允许执行,unsafe-hashes 可用于解决此等困境
-
unsafe-eval:允许将字符串当作脚本执行,例如 eval(), Function() 等
-
wasm-unsafe-eval:如果没有该字段,WebAssembly 将被拒绝执行
-
strict-dynamic:对于通过 nonce 值或 hash 值完成信任的脚本,其所加载的脚本也被信任
另外,CSP 中 report-uri 指令已经废弃,类似功能可通过 Content-Security-Policy-Report-Only
响应头实现。
3. 最佳实践
为网站部署 CSP,其实就像零信任网络一样,不应该信任任何外部未经验证的对象。
3.1 最低权限
CSP 的核心是尽可能使用白名单,从严格到宽松,尽量不要使用 *,因为会降低 CSP 的效果。
通常使用 default-src
指令作为默认的回退策略,
例如:
3.2 访问控制
避免使用内联脚本和样式,因为攻击者可以插入恶意代码。如果一定要用,可以使用上文中提到的 nonce 值或 hash 值。
避免使用类似 eval()
等不安全的 JavaScript 方法。
对于图片、字体、媒体等,最好使用相应指令明确信任源。
防止 HTTPS 页面加载 HTTP 资源,从而导致降级攻击,可以用 upgrade-insecure-requests
指令自动升级。
3.3 持续监控
部署时可以先紧后松,从最严格集合开始,根据实际效果不断放松。
另外需注意,CSP 应伴随网站内容结构改变而动态调整,因此定期的校验会是个不错的办法。
4. 总结
在文章的开始,就把零信任网络拿出来讲,是因为在我看来,CSP 的理念,和零信任网络异曲同工。
假设外部环境存在威胁,必要时授予最低权限,时刻观察行为和后果。
在安全(Security)和灵活(Flexible)中找到平衡。
悲观地去假设,采取一定的策略规避最坏情况的发生,然后乐观地生活。
我想,这可能就是最近经常谈及的底线思维吧。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2024/content-security-policy/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2024-11-20T09:44:17+08:00