关于 CSP 一个有趣的问题

首先, 在 CTF 中的 XSS 题目中, 有一个不知道从哪里看来的共识是, 如果整个站点有一处没有设置 CSP 头, 那么整个站点的 CSP 都形同虚设, 实际上, 这是  Chrome  处理的问题

一个简单的例子:

这段代码在 Chrome 中可以发现成功引入了外部  JS 从而 bypass 了 CSP, 但是在 Firefox 中, 这个请求被 CSP 拦截了

仔细观察可以发现, Firefox 在执行w.document.write()的时候, 打开的目标页的地址栏变成了 opener 的 url, Chrome 维持了目标页的 url, 所以显而易见, 在用 window.open 打开一个同源页面, 并且 opener 对目标页进行 DOM 操作的时候, Firefox 强制转换目标页的 security context 为 opener 的 context, 而 Chrome 维持了目标页的 context 原状

从 UXSS 的角度来看, 因为场景发生在同源中, 所以两种处理方法都没什么大问题, 但是从 CSP 的角度来看, Chrome 的处理方法有大问题

首先, Chrome 在修复 mathias 的一个 CSP bypass 的时候提到了一个处理方法:

这个方法提出后, 强行 bypass CSP 基本上就意味着 SOP 的 bypass, 有趣的是, mathias 抓住了其中了字眼main window navigation从而再次 bypass 了 CSP, 这里暂且不表, 回到正文, Chrome 对同源用 window.open 打开的页面使用了目标页自身的上下文, 导致了他不能有效继承 opener 已经设置正确的 CSP, 同时, 如果这个页面本身没有正确的 CSP 配置, 这将导致 CSP 的全盘崩溃

那么如何保证打开的页面没有正确的设置 CSP 呢? 一般来说, 设置 CSP 有两种方法, 第一种由页面本身设置, 例如 php 的 header(), html 的 meta 标签, 第二种则是由 http server 在代理时设置, 例如 Nginx 的 add_header

第一种, 静态文件不会自己设置 CSP, 例如最常见的 robots.txt, 以及各种 js 文件, json 文件, css 文件

第二种, Nginx 的 add_header 对404页面无效, 这是一个非常有趣的地方, 也就是说, 如果我们用 window.open 打开一个404页面, 那他有极大可能没有 CSP 的设置, 从而通过这个404页面, 我们能完全 bypass 站点的 CSP

那么这可能导致一个什么情况出现? 这里举一个例子

攻击者在 a.com/index.php 找到了一处 XSS, 但是他想获得 /admin/index.php 下的 cookie, 如果没有 CSP 的话用 iframe 引一个 /admin/ 子目录进来再用 iframe.contentWindow.document.cookie 就可以获得, 但是 /index.php 的 CSP 设置为 default-src ‘none’ 导致他不能直接引入, 这时用 window.open 打开一个404页面再引入 js 就可以成功拿到 cookie,233看上去很美好,实际上还是很鸡肋,需要绕过popup block,而且用window.open也能达到一样的效果,不过如果csp限制了connect-src这些倒是可以用用