Content Security Policy 与 XSS (跨域脚本攻击 )
Content Security Policy(CSP)实战指南:从原理到防XSS落地
作为前端开发者,跨域脚本攻击(XSS)是最头疼的安全漏洞之一——恶意脚本注入、用户数据泄露等风险防不胜防。而 Content Security Policy(CSP,网页安全政策)正是解决这个问题的“终极方案”:通过白名单机制明确允许加载的资源,从浏览器层面阻断恶意注入。今天结合阮一峰老师的教程和我们项目(YT网)的实际配置,用最通俗的方式讲清 CSP 的原理、配置和实战技巧。
一、先搞懂:CSP 到底是什么?
CSP 的核心本质是 白名单制度:开发者告诉浏览器“哪些域名的资源可以加载、哪些脚本可以执行”,浏览器严格按照规则拦截不符合要求的资源。
它的优势在于:
- 从源头阻断 XSS:即使攻击者找到漏洞,也无法注入未列入白名单的恶意脚本;
- 完全由浏览器执行:开发者只需配置规则,无需额外编写复杂的安全校验代码;
- 支持违规上报:能记录所有违反规则的行为,便于排查安全风险。
简单说:CSP 就像给网页装了一道“安全门禁”,只放白名单上的资源进门。
二、CSP 怎么启用?两种核心方式
启用 CSP 有两种常用方式,根据场景选择即可:
1. 通过 HTTP 响应头(推荐,服务端配置)
在服务器返回的 HTTP 头中添加 Content-Security-Policy 字段,优先级高于页面 meta 标签:
1 | |
2. 通过 HTML meta 标签(前端直接配置)
无需服务端改动,直接在 HTML 的 <head> 中添加 meta 标签,适合静态页面或快速验证:
1 | |
两种方式效果一致,生产环境建议用 HTTP 头(更灵活,可动态调整)。
三、核心指令详解:CSP 规则怎么写?
CSP 的规则由“指令 + 白名单值”组成,核心指令覆盖所有资源类型,下面是最常用的指令和含义:
| 指令 | 作用 | 示例 |
|---|---|---|
default-src |
所有资源的默认规则(未单独配置的指令都遵循此规则) | default-src 'self'(默认只允许当前域名) |
script-src |
限制外部脚本、内联脚本的执行(防XSS核心) | script-src 'self' *.yitong.com |
style-src |
限制样式表(外部CSS、内联样式) | style-src 'self' cdn.example.com |
img-src |
限制图片资源 | img-src 'self' data: https:(允许本地、base64、HTTPS图片) |
font-src |
限制字体文件 | font-src 'self' data: |
connect-src |
限制 AJAX、WebSocket 等网络请求 | connect-src 'self' api.yitong.com |
child-src |
限制 iframe、frame 等嵌入资源 | child-src https:(仅允许HTTPS协议的嵌入) |
object-src |
限制 Flash 等插件(几乎不用,建议设为'none') |
object-src 'none' |
upgrade-insecure-requests |
自动将 HTTP 资源请求转为 HTTPS(解决混合内容问题) | - |
关键:白名单值的写法(必懂)
每个指令后面的“值”就是白名单,支持以下格式:
'self':当前域名(含子域名),必须加单引号;'none':禁止加载任何资源,必须加单引号;- 具体域名:
*.yitong.com(所有子域名)、qq.com(固定域名); - 协议:
https:(所有HTTPS资源)、data:(base64资源); - 路径:
*.yitong.com/static/(仅允许该路径下的资源); - 多个值用空格分隔:
script-src 'self' *.yitong.com qq.com。
四、YT网 CSP 配置实战解析
我们项目用的是 meta 标签配置,逐句拆解每个规则的含义,你可以直接参考复用:
1 | |
1. default-src data: 'self' *.yitong.com 'unsafe-inline'
- 所有未单独配置的资源,默认遵循此规则;
- 允许加载:base64 资源(
data:)、当前域名及子域名('self' *.yitong.com)、内联样式/脚本('unsafe-inline'); - 用途:兼容项目中已有的内联样式和 base64 资源(如图片、字体)。
2. script-src 'self' *.yitong.com *qq.com 'unsafe-inline' 'unsafe-eval'
- 专门限制脚本执行(防XSS核心);
- 允许的脚本来源:
- 当前域名(
'self')、自身所有子域名(*.yitong.com); - QQ 相关域名(
*qq.com,比如集成的 QQ 登录、分享 SDK); - 内联脚本(
'unsafe-inline',兼容页面内的<script>标签和事件绑定); - 字符串转代码执行(
'unsafe-eval',兼容用eval、setTimeout('code')的旧代码)。
- 当前域名(
3. child-src https:
- 仅允许通过 HTTPS 协议加载 iframe、frame 等嵌入资源;
- 防止加载 HTTP 协议的恶意嵌入内容,避免混合内容风险。
4. upgrade-insecure-requests
- 自动将页面中所有 HTTP 资源请求转为 HTTPS;
- 解决“HTTPS 页面加载 HTTP 资源”被浏览器拦截的问题,无缝兼容旧链接。
五、script-src 特殊配置:避坑关键
script-src 是防XSS的核心,除了常规白名单,还有几个特殊值需要重点注意(必须加单引号):
1. 'unsafe-inline':允许内联脚本/样式
- 允许的场景:
<script>alert('test')</script>、onclick="xxx"、<style>xxx</style>; - 风险:可能被攻击者利用注入内联恶意脚本;
- 适用场景:老项目中有大量内联代码,无法一次性重构时(过渡方案)。
2. 'unsafe-eval':允许字符串转代码
- 允许的场景:
eval('1+1')、new Function('return 1')、setTimeout('alert(1)'); - 风险:攻击者可能通过字符串拼接注入恶意代码;
- 建议:能不用就不用,新项目尽量避免
eval类语法。
3. nonce/hash:更安全的内联脚本允许方式
如果想启用 CSP 又要保留部分内联脚本,推荐用 nonce 或 hash(比 'unsafe-inline' 安全):
- nonce 方式:服务端生成随机 token,脚本标签必须带该 token 才允许执行;
1
2# HTTP 头配置
Content-Security-Policy: script-src 'nonce-abc123xyz'1
2
3
4<!-- 页面内脚本必须带 nonce 属性 -->
<script nonce="abc123xyz">
console.log('仅带正确 nonce 的脚本能执行');
</script> - hash 方式:计算脚本内容的 hash 值,只有 hash 匹配才允许执行;
1
2# 脚本内容 alert('hello') 的 SHA256 hash
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
六、进阶功能:违规上报与测试模式
1. report-uri:记录违规行为
想知道哪些资源被 CSP 拦截、是否有恶意攻击尝试,可以配置 report-uri,浏览器会用 POST 方式上报违规信息:
1 | |
上报的 JSON 格式示例(包含违规页面、被拦截资源、违反的规则):
1 | |
2. Content-Security-Policy-Report-Only:测试模式
上线前怕配置错误导致正常资源被拦截?可以用“仅上报不拦截”模式测试:
1 | |
此时浏览器不会阻断资源加载,但会上报所有违反规则的行为,适合灰度测试。
七、避坑注意事项(必看)
default-src是默认规则:如果某个指令(如img-src)没配置,就会继承default-src的规则;- 指令重复无效:同一指令写多次,只有第一次生效(比如
script-src a.com; script-src b.com只生效a.com); 'self'不包含跨域子域名:a.yitong.com的'self'不包含b.yitong.com,需要显式写*.yitong.com;- **
object-src建议设为'none'**:现在基本不用 Flash 等插件,禁用后减少安全风险; - 避免过度放宽规则:尽量不用
'unsafe-inline'和'unsafe-eval',新项目优先用nonce/hash兼容内联脚本。
八、实战建议:从0到1配置 CSP
- 先测试再上线:先用
Report-Only模式运行1-2天,收集所有违规上报,修正规则(比如漏加的 CDN 域名); - 最小权限原则:只允许必要的资源,比如脚本只加业务必需的域名,不写
script-src *(等于没防护); - 必设核心指令:
script-src(防XSS)和object-src(禁用插件)必须配置,不能省略; - 兼容旧项目:如果老项目内联脚本太多,先加
'unsafe-inline'过渡,再逐步重构为外部脚本或nonce模式。
总结
CSP 是前端安全的“护城河”,核心就是通过白名单明确资源来源,从浏览器层面阻断 XSS 攻击。配置时不用追求“一刀切”,可以像我们项目这样,根据业务需求灵活调整(比如兼容第三方 SDK、内联资源),同时通过违规上报持续优化规则。
对于新项目,建议直接用 HTTP 头配置,禁用 'unsafe-inline' 和 'unsafe-eval',用 nonce/hash 兼容必要的内联脚本;对于老项目,先以“测试模式”收集违规信息,再逐步收紧规则,既保证安全又不影响业务。