jsonp的原理很简单,就是动态的创造script标签,然后利用script的src不受同源策略约束来跨域获取数据。
dvwa bypass CSP 的high级别就使用了JSONP
页面中存在的js文件:
function clickButton() {
var s = document.createElement("script");
s.src = "source/jsonp.php?callback=solveSum";
document.body.appendChild(s);
}
var solve_button = document.getElementById ("solve");
if (solve_button) {
solve_button.addEventListener("click", function() {
clickButton();
});
}
//前面的为动态创造script标签绑定事件
function solveSum(obj) {
if ("answer" in obj) {
document.getElementById("answer").innerHTML = obj['answer'];
}
} //收到服务端返回到插入页面的值
服务端代码:
<?php
header("Content-Type: application/json; charset=UTF-8");
if (array_key_exists ("callback", $_GET)) {
$callback = $_GET['callback'];
} else {
return "";
}
$outp = array ("answer" => "15");
echo $callback . "(".json_encode($outp).")";
?>
这里存在的问题是callback参数未经过过滤,可能构成XSS
令
?callback=alert(document.cookie)//
实现弹窗
值得注意的是,为什么浏览器会把JSONP接受到的数据当做js代码执行
普通的script标签内一般是js文件:
<script src="xxxx.js"></script>
但是作为JSONP的src里面放的是后台地址和参数
<script src="source/jsonp.php?callback=solveSum"></script>
所以先请求,请求到的数据会被浏览器作为js代码执行。
一开始还不太清楚为什么这个js会被执行,后来我想明白了,这就和请求普通的js文件一样,浏览器会把请求普通js的返回(js代码)作为代码执行,而请求source/jsonp.php?callback=solveSum返回的也是js代码,对于浏览器来说是没有任何区别的。
这里要注意,直接访问网址source/jsonp.php?callback=solveSum并不会导致回传代码的执行。
通过JSONP的跨域特性也可以进行CSP的绕过,对于禁用了inline js脚本的CSP来说,可以通过JSONP进行绕过。