CSP杂谈

0x00介绍

Content Security Policy (CSP)内容安全策略,是一个附加的安全层,有助于检测并缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。主要用于防御XSS,CSRF攻击,它通过加载唯一的受信任的CDN的脚本降低脚本注入的风险。一般通过HTTP头和<meta>标签来表达

0x01基本语句

我们常见的CSP的样式为

Content-Security-Policy: default-src  'self'; img-src *;script-src https://cdn.example.com/scripts/; object-src'un'
  1. default-src  表示资源的来历,self代表匹配同源的来源,这里表示只匹配同站的来源,none代表什么都不匹配
  2. img-src *;script-src https://cdn.example.com/scripts/; object-src'un' 表示允许本站的任意图片来源和https://cdn.example.com/scripts/下的脚本

<meta>的格式为

<meta http-equiv="Content-Security-Policy" content="script-src 'self'">

 

0x02 CSP测试指令

child-src

该指令控制嵌套上下文,例如<iframe><frame>导航和上下文的创建:

directive-name  = "child-src"
directive-value = serialized-source-list

该指令控制将填充框架或工作程序的请求,例如:

Content-Security-Policy: child-src https://example.com/

则如下的代码将返回错误,不匹配child-src源

<iframe src="https://example.org"></iframe>
<script>
  var blockedWorker = new Worker("data:application/javascript,...");
</script>

让我们看一个页面

<?php
header("Content-Security-Policy: child-src https://www.baidu.com/");
?>
<html lang="ch">
<head>
    <meta charset="utf-8" />
    <title>CSP test</title>
</head>
<body>
<iframe src="https://christa.top" style="width: 100%;height: 800px"></iframe>
        <script>
    var blockedWorker = new Worker("data:application/javascript,...");
</script>
</body>
</html>

我们可以看到因为CSP的拓展,网页的请求已经被阻止,而访问允许的页面则可以出现响应

同时使用<meta>标签也可以产生同样的效果

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; child-src https://www.baidu.com">

 

connect-src

该链接限制了其他传输或接收数据的请求,比如API fetch(),[XHR],<a>的ping等函数,同时还控制了WebSocket的连接

如下列的例子都会被限制

<a ping="https://christa.top">...
<script>
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'https://christa.top/');
  xhr.send();

  var ws = new WebSocket("wss://christa.top/");

  var es = new EventSource("https://christa.top/");

  navigator.sendBeacon("https://christa.top/", { ... });
</script>

 

default-src

该指令的值用作策略的默认源列表,使用default-src 'none';script-src 'self',脚本将请求将'self'用作匹配的源列表,其他请求将使用'none'

以下指令

Content-Security-Policy: default-src 'self'

等同于

Content-Security-Policy: connect-src 'self';
                         font-src 'self';
                         frame-src 'self';
                         img-src 'self';
                         manifest-src 'self';
                         media-src 'self';
                         prefetch-src 'self';
                         object-src 'self';
                         script-src-elem 'self';
                         script-src-attr 'self';
                         style-src-elem 'self';
                         style-src-attr 'self';
                         worker-src 'self'

因此,当default-src被设定时,所有的fetch指定将回退到default-src的值当中,同时该值还没有继承,如果一个script-src的值被指定了,那么default-src的指令不再有影响,向如下的头

Content-Security-Policy: default-src 'self'; script-src-elem https://christa.top

等同于

Content-Security-Policy: connect-src 'self';
                         font-src 'self';
                         frame-src 'self';
                         img-src 'self';
                         manifest-src 'self';
                         media-src 'self';
                         prefetch-src 'self';
                         object-src 'self';
                         script-src-elem https://christa.top;
                         script-src-attr 'self';
                         style-src-elem 'self';
                         style-src-attr 'self';
                         worker-src 'self'

因此,建立这种网站最好使用开始default-src的'none',并建立从设立一个我们允许拿到资源的特定的链接

到这里,我们基本可以猜测到CSP的基本语句,则简化后面的介绍

font-src

Content-Security-Policy: font-src https://christa.top

等同于

<style>
  @font-face {
    font-family: "Example Font";
    src: url("https://christa.top/font");
  }
  body {
    font-family: "Example Font";
  }
</style>

frame-src

Content-Security-Policy: frame-src https://christa.top/

等同于

<iframe src="https://christa.top/">
</iframe>

img-src

Content-Security-Policy: img-src https://christa.top/

等同于

<img src="https://christa.top/img">

manifest-src

Content-Security-Policy: manifest-src https://christa.top/

等同于

<link rel="manifest" href="https://christa.top/manifest">

media-src

Content-Security-Policy: media-src https://christa.top/

等同于

<audio src="https://christa.top/audio"></audio>
<video src="https://christa.top/video">
    <track kind="subtitles" src="https://christa.top/subtitles">
</video>

prefetch-src

Content-Security-Policy: prefetch-src https://christa.top/

等同于

<link rel="prefetch" src="https://christa.top/"></link>
<link rel="prerender" src="https://christa.top/"></link>

object-src

Content-Security-Policy: object-src https://christa.top/

等同于

<embed src="https://christa.top/flash"></embed>
<object data="https://christa.top/flash"></object>
<applet archive="https://christa.top/flash"></applet>

该object-src作用于代表一个所作的任何请求<object><embed>apple元件

script-src

该指令限制了脚本可以被执行的位置,不仅包括<script>元素中的URL,还包括可以触发脚本执行的内联脚本块和XSLT等内容,脚本管理五件事:

  1. 必须验证CSP是否阻止请求
  2. 必须验证CSP是否阻止请求的响应
  3. 内联<script>块必须验证CSP是否阻止元素的内联行为,除非每个策略允许内联脚本或者通过不指定script-src(或default-src)指令的设置
  4. 以下Javascript执行接收器在"unsafe-eval"上进行控制

             1 eval()

            2 Function()

            3 setTimeout()

           4 setInterval()

     5.导航的javascript:URL 必须通过script-src的检查

script-src-elm

该指令限制着所有的响应、script请求和script模块,与script-src的区别为

  1. script-src-elwm 适用于| type| 为"script" 和 “navigation” 的链接
  2. script-sec-elem 的值不用于在 'unsafe-eval'中进行接收器的检查
  3. script-src-elem 不用作worker-src指令的后备。该worker-src检查还依赖script-src指令。

script-src-attr

该指令适用于事件处理程序,如果存在,它会覆盖script-src的相关检查指令。

style-src

该指令被用于Document申请请求的位置,它管理着几件事情

  1. 样式请求必须验证CSP是否阻止请求,包括:1、样式请求源自<link>元素 2、源自 '@import'规则的样式  3、样式表请求源自LinkHTTP响应字段头
  2. 对样式的请求响应必须验证CSP是否阻止请求响应
  3. 内联<style>块必须验证CSP是否阻止元素的内联行为。除非每个策略都允许内联样式,否则样式将被阻止,无论是通过不指定style-src(或default-src)指令隐式地,还是通过指定“ unsafe-inline”,none-source或与内联块匹配的hash-source值显示它
  4. 一下CSS算法在unsafe-eval上进行控制:

                1. CSS插入规则

                2. CSS解析规则

               3. 解析CSS声明块

              4. 解析一组选择器

style-src-elem 、style-src-attr

script-src-elem style-src-attr大体上一致,皆在控制CSS响应函数地执行

worker-src

Content-Security-Policy: worker-src https://christa.top/

等同于

<script>
  var blockedWorker = new Worker("data:application/javascript,...");
  blockedWorker = new SharedWorker("https://christa.top/");
  navigator.serviceWorker.register('https://christa.top/sw.js');
</script>

还有一些基于插件和控制链接地CSP规则就不一一介绍啦。

0X03 CSP绕过

在xxx-src *表除了允许内联函数以外所有的url式请求,很容易造成CSRF漏洞,比如下的CSP

Content-Security-Policy

default-src 'none'; connect-src 'self'; frame-src *; script-src 'self' 'unsafe-inline'

发现由于iframe的内联不同源,故无法通过任何方式get cookie,不存在xss漏洞),但是可以利用这种方式构造CSRF漏洞,但同能看到script-src 'self' 'unsafe-line',可以看到普通的js脚本被拦截

但构造基本的xss语句则能执行

<script>alert(document.cookie);</script>

虽然不能使用外源的XSS,但能够构造cookie给自己的账号留言从而得到cookie,再看一个更加严格的CSP

Content-Security-Policy

default-src 'none'; connect-src 'self'; frame-src 'self'; script-src https://crhista.top/christa.js; style-src 'self' 'unsafe-inline' ; img-src 'self'

将来自本地域的script禁止了,但如果构造如下payload

<link rel="prefetch" href="httpss://christa.top/christa">

即可以发送请求包向客户端,但如过开发人员都设置了X-Frame-Options:Deny则我们可以在该网站的404站点进行探索,因为并不是所有的404页面都设置了CSP,用NGINX来说的话,我们只需要强制让NGINX返回 400 bad request,只需要使用/../访问上一级路劲中的资源,可以使用unicode替换。

frame=document.createElement(“iframe”);
frame.src=”/%2e%2e%2f”;
document.body.appendChild(frame);

这里的另一种可能的方法是传递不正确的unicode路径,如/%或/%%z。又或者传入超过Web服务器可以接受的最大长度的URL,因此使用

frame=document.createElement(“iframe”);
frame.src=”/”+”A”.repeat(20000);
document.body.appendChild(frame);

 

更多的绕过姿势慢慢探索吧。。

Reference

  1. https://www.w3.org/TR/CSP3/#csp-directives
  2. https://lorexxar.cn/2016/08/08/ccsp/#Bypass-CSP
  3. https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa