DVWA——Content Security Policy (CSP) Bypass学习笔记


前言

个人学习


一、Content Security Policy (CSP) Bypass是什么?

CSP(Content Security Policy,内容安全策略)是一个强大的XSS防御机制,但它并非牢不可破。当策略配置不当或与其他Web技术组合时,就可能出现绕过漏洞。

CSP通过Content-Security-Policy HTTP头或<meta>标签,以白名单形式告诉浏览器哪些资源是允许的,从而降低XSS风险。例如:

Content-Security-Policy: script-src 'self' https://trusted.cdn.com

这条策略告诉浏览器,只允许执行同源’self’以及来自https://trusted.cdn.com的JavaScript脚本

如果攻击者往网页里注入了 <script src="http://evil.com/hack.js"></script>,浏览器就会拒绝加载这个脚本,因为 CSP 不允许从 evil.com 加载

本练习/题目的目标是:在启用 CSP 的页面中,找到能够执行 JavaScript 的方法(通常源于 CSP 配置不当或实现缺陷),从而演示该配置的脆弱性。


二、步骤

1.Low

代码如下:

<?php

$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com hastebin.com www.toptal.com example.com code.jquery.com https://ssl.google-analytics.com unpkg.com cdn.jsdelivr.net digi.ninja ;"; // allows js from various trusted locations

header($headerCSP);

# These might work if you can't create your own for some reason
# https://cdn.jsdelivr.net/gh/digininja/csp_bypass/alert.js
# https://unpkg.com/@digininja/csp_bypass@1.0.0/index.js

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    <script src='" . $_POST['include'] . "'></script>
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:</p>
    <input size="50" type="text" name="include" value="" id="include" />
    <input type="submit" value="Include" />
</form>
<p>
    You will probably need to do some reading up on what some of the domains allowed by the CSP do and how they can be used.
</p>
';

源码对 HTTP 头定义了 CSP 标签,从而定义了可以接受外部 JavaScript 资源的白名单,允许的域名包括:self(同源)、pastebin.com、hastebin.com、toptal.com、example.com、code.jquery.com、ssl.google-analytics.com、unpkg.com、cdn.jsdelivr.net、digi.ninja

在这里插入图片描述
允许你从这些地址获取脚本https://cdn.jsdelivr.net/gh/digininja/csp_bypass/alert.js

该脚本的域名 cdn.jsdelivr.net 在 CSP 白名单中,因此浏览器允许加载并执行其内容。脚本内容为 alert(‘CSP Bypass’) 或其他类似代码

从而演示了 CSP 配置虽然限制了来源,但若白名单中包含可被攻击者利用的端点,仍能执行恶意代码

2.Medium

后端代码如下:

<?php

$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";

header($headerCSP);

// Disable XSS protections so that inline alert boxes will work
header ("X-XSS-Protection: 0");

# <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    " . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.</p>
    <input size="50" type="text" name="include" value="" id="include" />
    <input type="submit" value="Include" />
</form>
';

在 Medium 级别中,服务端返回的 CSP 响应头去掉了对 pastebin.com 等域名的白名单校验,改为使用 nonce(一次性随机数)机制进行限制。具体 CSP 策略如下:

script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA='

这意味着,页面中任何 <script> 标签必须携带正确的 nonce 属性,且其值与策略中给定的 nonce- 后面的字符串完全一致,脚本才会被浏览器执行。

因此,攻击者只需要在提交的脚本标签中显式添加 nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=" 即可绕过 CSP 限制。例如:

<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">document.write(document.cookie);</script>

关于 HTML nonce 属性的详细信息,可参考:
https://deepinout.com/html/html-questions/398_html_whats_the_purpose_of_the_html_nonce_attribute_for_script_and_style_elements.html


什么是 nonce 属性?

nonce(Number used once,一次性数字)是 HTML 为 <script><style> 元素提供的一个安全属性,用于配合内容安全策略(CSP)实现精确的内联脚本白名单。

工作原理
  1. 服务器在生成页面时,为每个需要执行的内联脚本生成一个不可预测的随机字符串(例如 TmV2ZXIg...)。
  2. 服务器将该字符串作为 nonce 属性的值写入 <script> 标签,并在 CSP 响应头中通过 script-src 'nonce-<随机值>' 告知浏览器该值是允许的。
  3. 浏览器加载页面后,只会执行 nonce 属性值与 CSP 头中指定值一致的 <script> 标签,从而阻止攻击者注入的任何不带正确 nonce 的恶意脚本
示例
Content-Security-Policy: script-src 'nonce-abc123'
<script nonce="abc123">alert('Trusted script');</script>   <!-- 允许执行 -->
<script>alert('Untrusted script');</script>               <!-- 阻止执行 -->
为什么 nonceunsafe-inline 安全?
  • unsafe-inline 允许所有内联脚本执行,完全破坏 CSP 防御。
  • nonce 只允许携带正确随机数的特定脚本,攻击者无法猜测该随机数,因此无法注入可执行的脚本。
注意事项
  • nonce必须足够随机(至少 128 位),每次页面刷新应重新生成,否则攻击者可复用已知的 nonce
  • 如果同时存在 unsafe-inlinenonce,现代浏览器会忽略 unsafe-inline,以 nonce 为准。
  • 对于外部脚本(<script src="...">),也可以使用 nonce 属性,但通常外部脚本通过域名白名单控制更常见。
    在这里插入图片描述

3.High

<?php
$headerCSP = "Content-Security-Policy: script-src 'self';";

header($headerCSP);

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    " . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p>
    <p>1+2+3+4+5=<span id="answer"></span></p>
    <input type="button" id="solve" value="Solve the sum" />
</form>

<script src="source/high.js"></script>
';
function clickButton() {
    var s = document.createElement("script");
    s.src = "source/jsonp.php?callback=solveSum";
    document.body.appendChild(s);
}

function solveSum(obj) {
    if ("answer" in obj) {
        document.getElementById("answer").innerHTML = obj['answer'];
    }
}

var solve_button = document.getElementById ("solve");

if (solve_button) {
    solve_button.addEventListener("click", function() {
        clickButton();
    });
}

本题目的 CSP 策略为 script-src 'self';,仅允许执行同源脚本,禁止内联脚本和外部非同源脚本。页面通过 JSONP 技术从 source/jsonp.php 获取数据,并调用回调函数 solveSum。攻击者需要让该 JSONP 接口返回恶意 JavaScript 代码,从而绕过 CSP 并执行任意代码。

利用方法

  1. 直接修改 jsonp.php 文件
    source/jsonp.php 的内容改为:

    <?php
    $callback = $_GET['callback'];
    echo $callback . '({"answer": 15}); alert(1);';
    

    当用户点击“Solve the sum”按钮时,动态加载的脚本会执行 alert(1)

  2. 通过 include 参数注入同源脚本(如果允许)
    虽然用户输入 $_POST['include'] 直接输出到页面,但内联脚本 <script>alert(1)</script> 因 CSP 禁止 unsafe-inline 而无法执行。但可以注入一个指向同源脚本的 <script src="..."> 标签,例如:

    <script src="source/jsonp.php?callback=alert(1)"></script>
    

    这要求 jsonp.php 输出 alert(1); 格式的响应。若 jsonp.php 原本只接受 callback 参数并输出 callback(...),则修改它后即可生效。

为什么能绕过 CSP?

  • script-src 'self' 允许从同源加载外部脚本。
  • jsonp.php 位于同源,因此其返回的脚本(包含恶意代码)会被浏览器执行。
  • 攻击者通过修改服务器端文件或控制 JSONP 接口的输出,实现了任意 JavaScript 执行,从而绕过 CSP。

在这里插入图片描述

POST 包是自己发的
  • 通过 HackBar 或类似工具,手动构造了一个 POST 请求,目标是 http://172.00.000.00/DVWA/vulnerabilities/csp/
  • POST 参数 include 的内容是:<script src="source/jsonp.php?callback=alert(document.cookie);"></script>
  • 这个请求的作用是:让服务器把这段 HTML/JS 代码输出到页面上,从而在浏览器中动态加载一个同源的脚本。
目的不是修改 callback 的“返回值”,而是控制 JSONP 的输出内容
  • jsonp.php 原本设计是用来做 JSONP 服务的,它会接收 callback 参数,然后输出类似 solveSum({"answer":15}) 的内容。
  • 通过修改 callback 参数的值(从 solveSum 改成 alert(document.cookie);),让服务器输出的内容变成了 alert(document.cookie);({"answer":15})
  • 这实际上是改变了 JSONP 响应的 JavaScript 代码,而不是修改返回值。因为 JSONP 的原理就是把 callback 参数的值当作函数名,然后调用它。你传入 alert(...),它就调用 alert
为什么弹窗了?
  • 浏览器加载 source/jsonp.php?callback=alert(document.cookie); 后,执行返回的脚本:alert(document.cookie);({"answer":15})
  • 这是一个合法的 JS 语句:先执行 alert 弹窗,然后执行一个对象字面量(无副作用)。
  • 由于 jsonp.php 没有对 callback 参数做白名单过滤,攻击者可以任意指定函数名,从而执行任意 JS 代码。
与 CSP 的关系
  • 当前 CSP 策略为 script-src 'self';,只允许加载同源脚本
  • jsonp.php 位于同源,所以浏览器允许加载并执行它
  • 通过 include 参数注入的 <script> 标签,其实也是加载同源资源,符合 CSP 策略
  • 因此成功绕过了 CSP 的限制,实现了 XSS 攻击
题目本意 vs 实际利用
  • 题目描述中说“Modify that page to run your own code”,本意是让你去修改服务器上的 source/jsonp.php 文件内容(比如直接写 alert(1);)。
  • 但实际测试发现,jsonp.php 本身存在callback 参数注入漏洞,你不需要修改文件,只需要通过参数就能注入恶意代码。这是 JSONP 接口的常见安全问题。

4.Impossible

function clickButton() {
    var s = document.createElement("script");
    s.src = "source/jsonp_impossible.php"; // 固定路径,无参数
    document.body.appendChild(s);
}

function solveSum(obj) {
    if ("answer" in obj) {
        document.getElementById("answer").innerHTML = obj['answer'];
    }
}

var solve_button = document.getElementById ("solve");

if (solve_button) {
    solve_button.addEventListener("click", function() {
        clickButton();
    });
}

这段代码是 Impossible 级别 的 CSP 防御实现,与之前的 Medium/High 级别有本质区别。下面详细解释其安全特性。

代码功能分析

  • 用户点击“Solve the sum”按钮时,动态创建一个 <script> 标签。
  • 该脚本的 src 属性被硬编码为 source/jsonp_impossible.php没有任何查询参数(例如没有 ?callback=)。
  • 脚本加载后,服务器应返回类似 solveSum({"answer": 15}) 的 JavaScript 代码,调用前端定义的 solveSum 函数。
为什么这是安全的?
特性说明
固定脚本路径不接受用户输入,攻击者无法通过参数注入恶意代码。
无回调参数避免了 JSONP 常见的 callback 参数注入漏洞。
同源加载符合 CSP script-src 'self' 策略,且内容完全由服务器控制。
无动态代码生成服务器返回的脚本是静态的(或至少不依赖用户输入),因此无法被篡改。
与之前级别的对比
级别技术安全性
Low无 CSP可直接注入任意脚本
MediumCSP + 域名白名单(含 pastebin.com 等)可利用白名单域名托管恶意脚本
HighCSP + JSONP 动态回调jsonp.php 未过滤回调函数名,可通过 callback=alert(1) 注入
ImpossibleCSP + 固定脚本路径(无参数)无法注入,完全安全
总结
  • 该代码通过移除用户可控的参数输入,彻底消除了 JSONP 注入的风险。
  • 即使攻击者能够控制页面的其他部分,也无法改变脚本加载的 URL 或注入额外的代码。
  • 这是防御 CSP 绕过和 JSONP 安全问题的标准做法:不要动态生成脚本 URL,不要使用不可信的回调函数名

总结

常见的 CSP 绕过方式

绕过方法原理示例
unsafe-inline允许内联脚本,可直接注入 <script>alert(1)</script>策略中包含 unsafe-inline
unsafe-eval允许 eval()setTimeout() 等,可将字符串转为代码执行。使用 eval(location.hash)
域名白名单过宽信任整个 https:*.example.com,攻击者可在任意 HTTPS 站点或子域名托管恶意脚本。利用 GitHub Gist、CDN 上的恶意文件
JSONP 接口信任的域名存在 JSONP 接口,可通过 callback 参数执行任意函数。script-src 'self' api.com,而 api.com/jsonp?callback=alert(1) 返回 alert(1);
<base> 标签劫持改变相对路径的解析,加载恶意脚本。注入 <base href="https://evil.com/">
表单劫持未设置 form-action,可将表单提交到攻击者服务器。注入 <form action="https://evil.com">
利用 CDN 上的“脚本小工具”允许的 CDN 上存在不安全的库(如 AngularJS 旧版),可构造表达式执行代码。使用 AngularJS 的沙箱逃逸
DNS 重绑定利用域名解析时间差绕过基于域名的白名单。较少见,但理论可行
泄露 nonce通过 CSS 注入或缓存攻击窃取合法的 nonce 值。利用 :has() 选择器读取 nonce 并发送到攻击者服务器

防御 CSP 绕过的建议

  1. 禁用 unsafe-inlineunsafe-eval,使用 noncehash 精确控制内联脚本。
  2. 使用具体域名,避免通配符 * 或过宽的关键字(如 https:)。
  3. 为 JSONP 接口添加白名单,限制 callback 参数只能为预定义的函数名。
  4. 设置 form-action 'self' 防止表单劫持。
  5. 设置 base-uri 'self' 防止 <base> 标签篡改。
  6. 启用 CSP 报告机制,监控违规行为并及时调整策略。
  7. 定期审计白名单域名,移除不再使用或不可信的源。
  8. 对敏感操作使用 nonce,且每次刷新页面重新生成不可预测的值。

CSP 是 XSS 防御的重要深度防御手段,但安全强度完全取决于配置的严格程度。如果配置不当(如允许 unsafe-inline、白名单过宽),攻击者仍能绕过并执行恶意代码。正确配置的 CSP 应遵循最小权限原则,避免使用不安全的指令,并结合 noncehash 精确放行合法脚本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sh3l6y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值