初窥CORB

Posted by UlyC on August 1, 2019

句子迷倒闭了23333 —— ulyc

初窥CORB

Cross-Origin Read Blocking (CORB) 跨域读锁

演示

任意打开一个网站, 将img标签的src改为另一个域名.就会出现CORB

CORB.png

在进一步解释CORB之前, 先来看几个概念

CPU预执行(CPU speculation execution)

众所周知,CPU执行计算的速度肯定是远大于它读取内存的速度的,这样的结果就是,CPU在对内存读取某些数据的时候,会闲置,这样就造成了浪费。

为了提高性能,现代基本大部分硬件制造商都引入了预执行这个机制来压榨CPU的性能。大概的意思如下,比如你写了一段代码:

if(somethingTrueOrFalse) {
  // TODO ...
}

逻辑上,这个 if 语句内部的代码是否执行,取决于 somethingTrueOrFalse 变量.

但是注意,这是逻辑上的,CPU在运行这段代码的时候,可不是这样子的。

它可能会直接跳过判定 somethingTrueOrFalse 是真是假的逻辑,直接执行 if 语句内部的代码,之后反过来再根据 somethingTrueOrFalse 的取值情况作出反应,如果为真,则保留执行结果,如果为假,则撤销执行结果。

这里对于预执行的描述是极度简化的,不过足够说明概念了。

CPU 预执行是芯片制造者决定的,为了提升 CPU 使用速度和效率而建的,预执行红利不是轻易就能放弃的,因此,目前或短期来看基本没可能改变。

旁路攻击 SCA(Side-Channel Attacks)

密码学中,旁道攻击又称侧信道攻击边信道攻击(英语:Side-channel attack)是一种攻击方式,它基于从密码系统的物理特征(如:时延,能耗,电磁,错误消息,频率等)中获取的信息而非暴力破解法或是算法中的理论性弱点。

早在 1956 年,英国已经利用 SCA 获取了埃及驻伦敦的加密机。

缓冲时延(Cache Timing)旁路

这是旁路攻击的一种,也是跟后来CORB的出现联系比较密切的一种, 这里只介绍这一种旁路攻击,

缓冲时延 攻击也叫计时攻击(timing attack),通过内存访问时间的不同来产生的旁路。

假设访问一个变量,这个变量在内存中,这需要上百个时钟周期才能完成,但如果变量访问过一次,这个变量被加载到缓冲(Cache)中了,下次再访问,可能几个时钟周期就可以完成了.

举个例子:

假设小 A 的账户密码是 gyease,小 B 想破解小 A 的密码,他可以这么做:

  • 首先他可以先输入 aaaaaa,之后记录一下从点击登录按钮到错误提示的间隔时间(虽然很短,假设有工具可以记录)
  • 之后再输入 baaaaa,同样记录时间
  • 重复以上过程直到 gaaaaa,会发现从点击登录按钮到错误提示的间隔时间稍微变长了一些
  • 之后小 B 即知道小 A 的密码第一位是 g,之后重复以上步骤即可破解小 A 的密码。

当然这里的例子很蠢,而且也过于理想化,但足够说明问题。反应快的读者可能马上就会知道为什么在观察 ‘gaaaaa’ 的测量结果后小 B 就会知道小 A 首位密码,这是因为执行校验密码是否正确的代码是需要时间的,因此在理想条件下,首位错误和首位正确第二位错误的反馈结果必然是后者时间略长。

幽灵和熔断漏洞(Spectre &Meltdown)

这是2018 年 1 月曝出来的漏洞.

熔断漏洞是幽灵漏洞的变种, 基本原理就是利用了上述的cpu预执行和缓冲时延旁路.

这可以称作史诗级的漏洞,具体实施比较复杂,可以参考 幽灵、熔断漏洞论文《利用旁道攻击读取特权内存》部分翻译

这里举一个简化的例子, 假设我们有以下代码:

if (x < arr1.length) {
  y = arr2[arr1[x]]
}

这个例子在参考链接的文章中你可能会多次见到,这里大概解释一下:

  • arr1 假设是一个比较小的数组,x 是一个我们定义的索引值变量
  • 正常情况下,如果 x 超过 arr1 的长度,程序是要崩溃的,因为它越界了,但是在预执行的前提下,CPU 可能会忽略越界的问题而执行 if 语句内部的代码
  • arr2 是我们提前声明的一个用来储存数据的数组,它储存于内存的另一个区域,它是连续的,而且我们强制它没有拷贝至缓存,只保存于内存(这点在视频中有提及,我这里强调一下)
  • 之后我们假设 arr1 中的位于 x 索引出的值是 k,那么在预执行的前提下,y = arr2[arr1[x]]等价于y = arr2[k]
  • 然后由于我们会把 arr2[k] 这个值付给另一个变量 y,这里其实算是一个访问值的操作,CPU 后将 arr2[k] 位于内存地址的值转入缓存中,而其余元素保留在内存中(因为并未访问)

之后,只需要遍历 arr2 这个数组,当发现某个索引上的值的访问速度远快于其他索引的访问速度时,这个索引”k”既是我们从越界内存中“偷”到的值。至此,一次攻击就完成了,理论上,利用这个漏洞,可以获取缓存区所有地址的值,其中很有可能包含敏感信息,比如密码什么的。

普通浏览器中,不同的站点可能共享同一个进程

在某些情况下,没有实现 Site Isolution 的普通浏览器会出现一个进程里面同时运行多个站点的代码,这就让恶意站点有机可乘。比如恶意站点 a.dd.com 在自己的代码中嵌入 <iframe src="https://v.qq.com" frameborder="0"></iframe>,这时,普通浏览器就会把带有恶意站点 a.dd.com 的恶意代码 和 v.qq.com 放在同一个内存中运行。

share_process.png

如何预防 Spectre 和 Meltdown 漏洞呢?

漏洞三大关键点是 CPU 预执行、SCA 和 共享进程。

预防就得从这三个方面着手。先看 SCA,算法运行时间的变化本质就是源于数据处理,根据时间变化推测运算操作和数据存储位置,因此 SCA 可预防性极低。

再看 CPU 预执行,性能至少提高 10%,一片可观的红利,芯片厂商如何舍得放弃。

如此,只能针对共享进程下手了,Site Isolation 便是剥离共享进程的一项技术,采用独立站点独立进程的方式实现,降低漏洞的威胁。

Site Isolation

站点隔离保证了不同站点页面始终被放入不同的进程,每个进程运行在一个有限制的沙箱环境中,在该环境中可能会阻止进程接收其它站点返回的某些特殊类型敏感信息,恶意站点不再和正常站点共享进程,这就让恶意站点窃取其它站点的信息变得更加困难。

从 Chrome 67 开始,已默认启用 Site Isolation,CORB 是其中一项很重要的功能 ,属于一种新的网络平台安全策略。

img

经验证,Site Isolation 关于进程独立的原则是 只要一级域名一样,站点实例就共享一个进程,无论子域名是否一样。如果使用 iframe 嵌入了一级域名不一样的跨域站点,则会生成一个新的进程维护该跨域站点运行,这一点同前文介绍的普通浏览器共享进程不同。

这是 Site Isolation 的进程设计,那么其中的 CORB 扮演了什么角色呢?

在同源策略下,Site Isolation 已经很好地隔离了站点,只是还有跨域标签这样的东西存在,敏感数据依旧会暴露,依旧会进驻恶意站点内存空间。 有这样一个场景,用户登录某站点 some.qq.com后,又访问了 bad.dd.com 恶意站点,恶意站点有如下代码,<script src="some.qq.com/login">,跨域请求了原站点的登录请求,此时,普通浏览器会正常返回登录后的敏感信息,且敏感信息会进驻 bad.dd.com 内存空间。好不容易站点隔离把各个站点信息分开了,这因为跨域又在一起了。这个时候怎么办呢?

CORB 来了。

CORB(Cross-Origin Read Blocking)

chromium 文档中关于它的定义:

an algorithm by which dubious cross-origin resource loads may be identified and blocked by web browsers before they reach the web page.

浏览器在加载可以跨域资源时,在资源载入页面之前,对其进行识别和拦截的算法。

这样一来, 敏感信息既不会暴露于浏览器,也不会进驻内存空间,安全性就得到了一定的保证。

哪些内容类型受 CORB 保护

当前有三种内容类型受保护,分别是 json、html 和 xml。

关于如何针对每种内容类型 CORB 如何对其进行保护,文档中有详细的章节进行介绍,这里就不多说了。

CORB 发生时机

当跨域请求回来的数据 MIME type 同跨域标签应有的 MIME 类型不匹配时,浏览器会启动 CORB 保护数据不被泄漏,被保护的数据类型只有 html xmljson。很明显 <script><img> 等跨域标签应有的 MIME type 和 htmlxmljson 不一样。

MIME type (Multipurpose Internet Mail Extensions)

MIME type 同 CORB 有着相当紧密的关系,可以说 CORB 的产生直接依附 MIME 类型。因此,阅读本文前,有必要先理解一下什么是 MIME type。

MIME 是一个互联网标准,扩展了电子邮件标准,使其可以支持更多的消息类型。常见 MIME 类型如:text/html text/plain image/png application/javascript ,用于标识返回消息属于哪一种文档类型。写法为 type/subtype。 在 HTTP 请求的响应头中,以 Content-Type: application/javascript; charset=UTF-8 的形式出现,MIME typeContent-Type 值的一部分。如下图,

img

内容嗅探技术(MIME sniffing)

内容嗅探技术是指 当响应头没有指明 MIME type 或 浏览器认为指定类型有误时,浏览器会对内容资源进行检查并执行,来猜测内容的正确MIME类型。嗅探技术的实现细节,不同的浏览器在不同的场景下有不同的方式,本文不做详述。详细内容参见:https://www.keycdn.com/support/what-is-mime-sniffing

如何禁用

服务器在响应首部添加 X-Content-Type-Options: nosniff,用来告诉浏览器一定要相信 Content-Type中指定的 MIME 类型,不要再使用内容嗅探技术探测响应内容类型。该方法仅对 <script><style>有效。

官方解释:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing

浏览器如何判断响应内容是否需要 CORB 保护?

CORB 会根据如下步骤来确定是否对 response 进行保护(如果响应的内容格式是 json、html 或者 xml):

  • 如果 response 包含 X-Content-Type-Options: nosniff 响应头部,那么如果 Content-Type 是以下几种的话, response 将受 CORB 保护:
    • html mime type
    • xml mime type(除了 image/svg+xml)
    • json mime type
    • text/plain
  • 如果 response 的状态是 206,那么如果 Content-Type 是以下几种的话, response 将受 CORB 保护:
    • html mime type
    • xml mime type(除了 image/svg+xml)
    • json mime type
  • 否则,CORB 将尝试探测 response 的 body:
    • html mime type,并且探测结果是 html 内容格式,response 受 CORB 保护
    • xml mime type(除了 image/svg+xml), 并且探测结果是 xml 内容格式,response 受 CORB 保护
    • json mime type,并且探测结果是 json 内容格式,response 受 CORB 保护
    • text/plain,并且探测结果是 json、html 或者 xml 内容格式,response 受 CORB 保护
    • 任何以 JSON security prefix 开头的 response(除了 text/css)受 CORB 保护

这里值得一提的是,对于探测是必须的,以防拦截了那些依赖被错误标记的跨源响应的页面(比如,图片资源但是格式却被标记为 text/html)。如果不进行格式探测,那么会有16倍以上的 response 被拦截。

HTML MIME typeXML MIME typeJSON MIME type 的出现能理解,为什么 text/plain 类型也会在保护范围内?

因为 当 Content-Type 缺失的时候,响应内容 MIME type 有可能就是 text/plain;且据可靠数据显示, HTML, JSON, or XML 有时候也会被标记为 text/palin。如,

data.txt

{
  "ret_code": 0,
  "msg": "请求成功!",
  "data": [1, 2, 3, 4, 5]
}

server.js

app.get('/file', function getState(req,res,next){
  // res.type('json')
  res.sendfile(`${__dirname}/public/data.txt`)
})

如上代码,启动 server.js ,Chrome 浏览器跨域标签

img

CORB 如何拦截一个响应

当一个 response 被 CORB 保护时,它的 body 会被覆盖为空,同时 headers 也会被移除(当前 Chrome 仍然会保留 Access-Control-* 相关的 headers)。

关于 CORB 的工作方式,一定要和 CORS 做区分,因为它要防止这些被拦截的数据进入渲染当前页面进程的内存中,所以它一定不会被加载并读取。

这不同于 CORS,因为后者会做一些过滤操作,数据虽然不可被加载,但是可能仍然保留在渲染进程的内存中。

对于其他 web 平台特性的影响

这里仍然是翻译部分文档中的内容,因为本身写的就很细致了。

CORB 不会影响以下技术场景:

  • XHR and fetch()
    • CORB 并不会产生显而易见的影响,因为 XHR 和 fetch() 在响应中已经应用了同源策略(比如:CORB 应该仅会拦截那些因缺少 CORS 而发生跨域 XHR 错误的 response)
  • Prefetch
    • CORB 会拦截那些到达跨域渲染进程的 response body,但是不会阻止那些被浏览器进程缓存的 response body(然后传递到另一个同源渲染进程)。
  • Tracking and reporting
    • 当前存在各种各样的技术,尝试对记录用户访问的服务器发送 web 请求,以检查用户是否已访问某些内容。该请求经常使用隐藏的 img 标签进行发送(我前文提及了),然后服务器以 204 状态码或者 html 文档进行响应。除了 img,还可以使用类似 script、style 和别的可用标签。
    • CORB 不会对这些技术场景造成影响,因为它们不会依赖于服务器返回响应的内容。这一点同样使用与其他类似的技术场景和 web 特性,只要它们不关心响应即可,比如:beacons,ping,CSP违规报告 等。
  • Service workers
    • Service workers 可以拦截跨源 requests 并在其内部人为地构建 response(没有跨源和安全边界),CORB 不会拦截它们。
    • 当 Service workers 确实缓存了一些跨源的 responses 时,由于这些 responses 对于调用者来讲是透明的,因此 CORB 会拦截它们,但是这并不需要对 Service Worker 作出任何改变。
  • Blob and File API
    • 即使没有 CORB 的话,获取跨源的 blob URLs 当前也会被拦截。
  • Content scripts and plugins
    • 它们所属的范围并不含在 CORB 的职责内 —— CORB 假设已经有某种合适的安全策略或安全机制存在于这些 content scripts 和 plugins 中(比如 Adobe Flash 已经实现了类似 CORB 的机制,通过 crossdomain.xml)。

PS

需要注意的是, 幽灵和熔断漏洞是硬件层面的,软件层面只能做很有限的防护.

后记

本文基本只是将参考链接中的文章 按照我自己容易理解的方式重组了一下, 版权归链接中原作者所有。

参考链接

[1].30 分钟理解 CORB 是什么

[2].给程序员解释Spectre和Meltdown漏洞

[3] 妖艳货的技术博客 Cross-Origin Read Blocking (CORB)


知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。