30 分钟理解 CORB 是什么-程序员宅基地

技术标签: ViewUI  json  javascript  

写在前面

前些日子在调试 bug 的时候,偶然发现这么一个警告:

Cross-Origin Read Blocking (CORB) blocked cross-origin response https://www.chromium.org/ with MIME type text/html. See https://www.chromestatus.com/... for more details.

我当前的 chrome 版本是 v68,如果是 v66 或更低版本可能提示的警告信息略有不同。印象中只对 CORS 比较熟悉,CORB 是个什么鬼?好奇心迫使我想要了解一下它到底是什么,于是暂时把手头工作放下查了一些资料并花时间汇总了一下,就有了这篇文章。

再介绍 CORB 是什么以及有什么用之前,需要先了解一些背景知识以做铺垫,下面进入正文。

旁路攻击(side-channel attacks)

首先需要了解的是旁路攻击这个术语,关于术语本身的解释,可以去维基百科搜索。简单讲的话,就是从软件系统的物理实现层获取信息进行攻击的手段,软件系统在正常运行时,产生了一些边缘特征,这些特征可以体现一些隐私信息。

这么说可能略显抽象,就拿后文视频链接中列举的例子说明一下,假设小 A 的账户密码是 gyease,小 B 想破解小 A 的密码,他可以这么做:

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

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

这就是一个比较典型的旁路攻击类型,专业的名称叫做计时攻击(timing attack),有兴趣的可以上网搜索了解详情。

预执行(speculation execution)

之后再来了解预执行这个概念,电脑之所以可以执行我们所编写的代码,其背后是由若干硬件协同工作的结果。其中两个比较重要的,一个是内存,一个是CPU。众所周知,CPU执行计算的速度肯定是远大于它读取内存的速度的,这样的结果就是,CPU在对内存读取某些数据的时候,会闲置,这样变造成了浪费。为了提高性能,现代基本大部分硬件制造商都引入了预执行这个机制来压榨CPU的性能。大概的意思如下,比如你写了一段代码:

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

逻辑上,这个 if 语句内部的代码是否执行,取决于 somethingTrueOrFalse 变量,但是注意,这是逻辑上的,CPU在运行这段代码的时候,可不是这样子的。它可能会直接跳过判定 somethingTrueOrFalse 是真是假的逻辑,直接执行 if 语句内部的代码,之后反过来再根据 somethingTrueOrFalse 的取值情况作出反应,如果为真,则保留执行结果,如果为假,则撤销执行结果。

这里对于预执行的描述是极度简化的,不过足够说明概念了。如果有兴趣可以上网搜索相关文章,尤其是预执行策略方面的,我看了一些,没看完,感觉和AI有的一拼(题外话)。

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

这个漏洞是在今年 1 月份被报道出来的,是硬件系统层面的漏洞。关于这个漏洞本身,网上已经有专业的论文对其进行了详尽的介绍,有兴趣可以自行搜索阅读,这里就不展开说了。简单讲,就是结合上文提及的两个概念的两种实际攻击方法。

这里还需要再说一下 CPU 读取数据的方式,CPU 除了利用预执行来提供性能,它本身在从内存读取数据的时候,还会涉及一个缓存的概念。从缓存读取数据的速度是大于内存的,当 CPU 发现将要读取的一个数据在缓存中存在时,它会直接从缓存中读取,这样同样可以提高性能,但是缓存很小同时也很昂贵,所以缓存的大小无法与内存相比。同时,每个程序运行时,CPU 为了防止进程间互相保持独立,它们都拥有属于自己的某块内存区域,假设程序 A 存在一条想要直接越界访问程序 B 内存的指令,这在 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 这个数组,当发现某个索引上的值的访问速度远快于其他索引的访问速度时,这个索引既是我们从越界内存中“偷”到的值。至此,一次攻击就完成了,理论上,利用这个漏洞,可以获取缓存区所有地址的值,其中很有可能包含敏感信息,比如密码什么的。

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.

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

这里可能有人会问,这和上面说的一堆又有什么关系呢?是这样的,Chrome浏览器在处理不同 tab 和不同页面时,会将为它们划分不同的进程,而且受制于同源策略的影响,这些页面之间本应该互不干扰。但是我们知道,同源策略虽然牛逼,但浏览器中仍然存在一些不受制于它约束的 api、标签,比如常见的 img、iframe 和 script等等。诸如以下代码,不知道看文章的诸位有没有写过,反正我是写过,或者说遇见过:

<img src="https://foo/bar/baz/">

有人可能会问,一个 img 标签你 src 属性不填图片的 uri,你是不是傻。其实不是这样的,有时候对网站做一些跟踪和分析时,确实会这么写,因为浏览器会往https://foo/bar/baz/这个地址发送一个 GET 资源的请求,在服务端我们可以利用这个请求做一些追踪的逻辑,同理 script 也可以完成需求。但是这么做的后果就是,虽然 img 帮我们发送了这个请求,但是它却没有得到所期望格式的资源,所以这里实际可以算作一种错误或者异常。而一些攻击者可以利用这一点,比如,在页面嵌入下面的代码:

<img src="https://example.com/secret.json">

来加载跨域私密文件,因为 img 不受同源策略的制约,这个请求是可以发出去的,服务器响应返回后,显然 secret.json 不是一个图片格式的资源,img 不会显示它,但是并不代表负责渲染当前页面的进程的内存中没有保留关于 secret.json 的数据。因此攻击者可以利用上文中提及的漏洞,编写一些代码来“偷”这些数据,从而造成安全隐患。

而 CORB 的作用就是当浏览器尝试以上面代码的方式加载跨域资源时,在资源未被加载之前进行拦截,从而提升攻击者进行幽灵攻击的成本,这里之所以是说提升成本还非彻底解决是因为这个漏洞是基于硬件层面的,所以软件层面只能做有限的修复,有的人可能马上会说,那 CPU 直接去掉或者用户放弃使用预处理功能不就好了吗?理论上是这样的,但是这将导致预处理带来的性能红利瞬间消失,而且 CPU 的架构设计也不是一天两天就能改的,而且就算改了也没办法一下普及。

哪些内容类型受 CORB 保护

当前有三种内容类型受保护,分别是 json、html 和 xml。关于如何针对每种内容类型 CORB 如何对其进行保护,文档中有详细的章节进行介绍,这里就不多说了。我浏览了一遍,大体的规则均是对内容格式进行一些有针对性的校验,以确认它确实是某个内容类型。这个校验结果最终影响 CORB 的运作方式。

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 被拦截。

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)。

总结

大概就这么多,读到这里,应该对 CORB 能够有一个初步的认识和把握了,以及它所需要解决的问题。最后我列举了我写这篇文章之前阅读的文章或者视频,有些需要自备梯子,有些不要。尤其推荐那个 B 站的视频,算是讲的最生动形象的了。另外 Chrome 的文档也很详细,只是有些长,需要耐心慢慢读完。

以上,如有错误,还望指出,大神轻喷。

参考链接

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34388207/article/details/88747606

智能推荐

Efficientdet_efficientdet 大模型-程序员宅基地

文章浏览阅读1.6k次,点赞2次,收藏10次。一文读懂EfficientDet今年年初Google Brain团队在 CVPR 2020 上发布了 EfficientDet目标检测模型, EfficientDet是一系列可扩展的高效的目标检测器的统称, 其精度与速度全面领先于YOLO V3, MaskRCNN, RentinaNet, NAS-FPN这些常见目标检测模型. EfficientDet-D7 的性能更是非常的惊人, 在 326B FLOPS, 参数量 52 M的情况下, COCO 2017 validation 数据集上取得了 51.0_efficientdet 大模型

Phpstudy v8.0使用Redis_phpstudy redis-程序员宅基地

文章浏览阅读6.7k次。1.应用场景快速安装并启动Redis进行开发. 2.学习/操作 1.安装Redis服务器 如图所示, 点击安装redis安装即可. 2.安装redisClient[用于连接redis服务器进行操作, 跟连接数据库是一样的] 切换到redis管理工作, 点击安装redisClient即可. ..._phpstudy redis

常用数据结构及算法_常用的结构体及算法 site:blog.csdn.net-程序员宅基地

文章浏览阅读520次。翻译来自:https://github.com/kdn251/interviews#live-coding-practice数据结构Linked List链表是数据元素的线性集合,称为节点,每个元素通过指针指向下一个节点。 它是由一组节点组成的数据结构,它们一起表示一个序列。单链表 : 其中每个节点指向下一个节点,而最后一个节点指向null。双向链表 : 其中每个节点具有两个指针p,n,使得p_常用的结构体及算法 site:blog.csdn.net

Node.js基础-程序员宅基地

文章浏览阅读1w次。NodeJS是什么node.js是一个异步的事件驱动的JavaScript运行时https://nodejs.org/en/node.js特性其实是JS的特性:非阻塞I/O事件驱动node历史 — 为性能而生并发处理多进程 - C Apache多线程 - java异步IO - js协程 - lua openresty go deno- go TS与前端的不同JS核心语法不变前端 BOM DOM后端 fs http buffer event os运行node程序

Django简单入门_django 简单-程序员宅基地

文章浏览阅读338次。MTV模式:Model 模型,与数据库交互Template 模板,HTMLViews 视图,用于处理请求,返回响应一、安装Django1.11.8pip install django==1.11.8二、创建Django项目(一)使用cmd创建进入Django项目目录下(或自定义任何目录)django-admin startproject 项目名(二)使用pycharm创建..._django 简单

获取url参数的方法-程序员宅基地

文章浏览阅读2.1k次。获取url参数主要有两种方法第一种是利用字符串的分割方法,将url以“&”和“=”做分割,得到参数数组,然后再利用数组的迭代方法中的filter()方法,筛选并返回我们需要的数据。function GetQueryString(name) { //获取url中“?”后边的部分,并将结果以“&”分割成数组 var paras = url.split('?')[1].spli_获取url参数的方法

随便推点

阿里云centos7mysql 5.6 的安装步骤记录_centos7 mysql5.6 there is no such grant defined fo-程序员宅基地

文章浏览阅读339次。阿里云centos7操作命令记录阿里云centos7mysql 5.6 的安装步骤记录列出所有被安装的rpm package# rpm -qa | grep mariadb查看是否安装mariadbrpm -qa | grep mariadbrpm -qa | grep mariadb-server查看安装的mysql rpm -qa | g_centos7 mysql5.6 there is no such grant defined for user 'slave1' on host

ie6 offsetWidth/offsetHeight无效-程序员宅基地

文章浏览阅读232次。ie6 offsetWidth/offsetHeight无效可采用手动获取:offsetWidth=parseInt(node.style.width)+parseInt(node.style.paddingLeft)+parseInt(node.style.paddingRight);offsetHeight=parseInt(node.style.height)+parseInt(node.s..._ie6 offsetwidth

整数规划之分支切割算法-程序员宅基地

文章浏览阅读6.2k次,点赞16次,收藏62次。目录1. 分支切割算法简介2.分支切割原理,及需要考虑的方面3.分支切割算法的关键4.总结1. 分支切割算法简介分支切割算法,即branch and cut,是branch and bound分支定界+cutting plane割平面。同理我们类比分支定价branch and price,是branch and bound分支定界+column generation列生成。两者都是精确算法。解决的是整数规划问题。我们通过松弛IP得到LP,最好松弛得到的LP就是整数解。但是实._分支切割算法

关于log4j的几个问题_log4jloggeradapter.isdebugenabled为false log4j.xml-程序员宅基地

文章浏览阅读324次。日志项目中的问题代码 private void doBissness() { String account = "LEON"; String cardNum = "XXX0288"; doDebug("account is " + account); doDebug("card num is " + cardNum); // do something } pri_log4jloggeradapter.isdebugenabled为false log4j.xml

R语言ggplot2坐标轴中英文名称_怎么调整中英文坐标轴标题在r-程序员宅基地

文章浏览阅读1.5w次,点赞4次,收藏28次。1.英文标题在ggplot2画图时用labs()设置坐标轴标题,比如给一个频数分布直方图设置标题:ggplot(data=travel)+geom_histogram(aes(x=price1))+ labs(x="price",y="count")2.中文标题有的时候如果输入中文,如下图,就会出现坐标轴文字不显示的情况。ggplot(data=travel)+geom_hist..._怎么调整中英文坐标轴标题在r

ocr文字识别html,在线OCR 随时随地轻松搞定文字识别-程序员宅基地

文章浏览阅读756次。文/王新禧大家平常扫描识别一些文字资料的时候,通常会用到OCR文字识别软件,不过有时候手头没有这类软件或者懒得安装,这就需要笔者推荐的在线OCR网站来帮忙了。在线OCR识别网站(www.netocr.net)应用了清华大学研制的国际领先OCR识别技术,支持TIF、BMP、JPG等多种常见图像格式,能识别出纯英文、简繁体中文、日文、韩文以及手写体和中英文混排的文本图像。通过它,可以从此告别OCR软件..._什么软件可以识别图片内容html代码

推荐文章

热门文章

相关标签