Cross-Origin Read Blocking (CORB)

本文的開始源於落地頁項目中遇到的 Chrome 控制檯 warn 提示,擔憂影響頁面渲染,特此弄個究竟。提示以下,javascript

Cross-Origin Read Blocking (CORB) blocked cross-origin response https://www.example.com/example.html with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details.
複製代碼

除非特殊說明,不然本文中的瀏覽器均指 Chrome Browserhtml

前言

本文將從如下幾個方面對 CORB 進行探討,java

  • 什麼是 CORB
  • 爲何會產生 CORB
  • 什麼狀況下會出現 CORB
  • 出現 CORB 時,咱們能夠如何看待

CORB 發生時瀏覽器表現

CORB 是一種判斷是否要在跨站資源數據到達頁面以前阻斷其到達當前站點進程中的算法,下降了敏感數據暴露的風險。node

- Chrome 瀏覽器提示

當請求發生 CORB 時,瀏覽器控制檯會打印以下警告內容,git

Content-Type: MIME

Cross-Origin Read Blocking (CORB) blocked cross-origin response https://www.example.com/example.html with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details
複製代碼

chrome 66或這個版本以前,提示信息有細微不一樣,程序員

Blocked current origin from receiving cross-site document at https://www.example.com/example.html with MIME type text/html
複製代碼

當請求的響應結果自己就出錯或爲空時,早期版本 Chrome 依舊會出現上述提示,但 Chrome 69 以後的版本再也不出現上述提示。下文實驗一和實驗二驗證了該描述。github

- Chrome 瀏覽器行爲

The response body is replaced with an empty body. // 響應數據置爲空
The response headers are removed. // 移除響應請求頭
複製代碼

CORB 啓動時,雖然響應結果會被置空,可是請求的服務仍然成功,`status: 200`。好比:使用 `img` 標籤上報頁面監控數據,儘管響應結果爲空,但請求依舊發送成功,服務器亦正常響應。下文實驗一已驗證。 web

爲何會有 CORB 的出現?

簡單來講,就是出現了一些網絡安全漏洞,爲防止漏洞肆虐,便出現了站點隔離(Site Isolation),CORB 則是其中的一種實現策略。算法

Spectre 和 Meltdown 漏洞

當惡意代碼和正常站點存在於同一個進程時,惡意代碼即可以訪問進程內的內存,進行一系列訪問攻擊,此時,惡意代碼竊取數據的惟一難點在於不知道敏感數據的具體存儲位置,但經過 CPU 預執行 和 SCA 能夠一步步 試探 出來。詳細瞭解可參看: zhuanlan.zhihu.com/p/32784852chrome

什麼是 CPU 預執行?

if(condition)
   do_sth();
複製代碼

CPU 執行速度大於內存讀取速度,爲了提高 CPU 使用率,在從內存中讀取 condition 完成以前,CPU 就已經開始執行下文內容。即無論 if 條件是否返回 true,CPU 都會提早執行裏面的語句do_sth()。 CPU 預執行是芯片製造者決定的,爲了提高 CPU 使用速度和效率而建的,預執行紅利不是輕易就能放棄的,所以,目前或短時間來看基本沒可能改變。

普通瀏覽器中,不一樣的站點可能共享同一個進程

在某些狀況下,沒有實現 Site Isolution 的普通瀏覽器會出現一個進程裏面同時運行多個站點的代碼,這就讓惡意站點有隙可乘。好比惡意站點 a.dd.com 在本身的代碼中嵌入 <iframe src="https://v.qq.com" frameborder="0"></iframe>,這時,普通瀏覽器就會把帶有惡意站點 a.dd.com 的惡意代碼 和 v.qq.com 放在同一個內存中運行。

共享進程

SCA(Side-Channel Attacks) 旁道攻擊

簡單來講,就是利用程序運行時,系統產生的一些物理特徵(如:時延,能耗,電磁,錯誤消息,頻率等)進行推測型攻擊。看起來有點難以想象,但早在 1956 年,英國已經利用 SCA 獲取了埃及駐倫敦的加密機。

緩衝時延(Cache Timing)旁路是經過內存訪問時間的不一樣來產生的旁路。假設訪問一個變量,這個變量在內存中,這須要上百個時鐘週期才能完成,但若是變量訪問過一次,這個變量被加載到緩衝(Cache)中了,下次再訪問,可能幾個時鐘週期就能夠完成了,可根據這種訪問速度竊取特定數據。Spectre 和 Meltdown 漏洞即是利用了這種特性。

如何預防 Spectre 和 Meltdown 漏洞呢?

漏洞三大關鍵點是 CPU 預執行、SCA 和 共享進程。預防就得從這三個方面着手。先看 SCA,算法運行時間的變化本質就是源於數據處理,根據時間變化推測運算操做和數據存儲位置,所以 SCA 可預防性極低。再看 CPU 預執行,性能至少提升 10%,一片可觀的紅利,芯片廠商如何捨得放棄。如此,只能針對共享進程下手了,Site Isolation 即是剝離共享進程的一項技術,採用獨立站點獨立進程的方式實現,下降漏洞的威脅。

Site Isolation

站點隔離保證了不一樣站點頁面始終被放入不一樣的進程,每一個進程運行在一個有限制的沙箱環境中,在該環境中可能會阻止進程接收其它站點返回的某些特殊類型敏感信息,惡意站點再也不和正常站點共享進程,這就讓惡意站點竊取其它站點的信息變得更加困難。從 Chrome 67 開始,已默認啓用 Site Isolation。

Site Isolation

經驗證,Site Isolation 關於進程獨立的原則是 只要一級域名同樣,站點實例就共享一個進程,不管子域名是否同樣。若是使用 iframe 嵌入了一級域名不同的跨域站點,則會生成一個新的進程維護該跨域站點運行,這一點同前文介紹的普通瀏覽器共享進程不一樣。更詳細的內容參看 www.yaoyanhuo.com/blog/site_i…

這是 Site Isolation 的進程設計,那麼其中的 CORB 扮演了什麼角色呢?

在同源策略下,Site Isolation 已經很好地隔離了站點,只是還有跨域標籤這樣的東西存在,敏感數據依舊會暴露,依舊會進駐惡意站點內存空間。 有這樣一個場景,用戶登陸某站點 some.qq.com後,又訪問了 bad.dd.com 惡意站點,惡意站點有以下代碼,<script src="some.qq.com/login">,跨域請求了原站點的登陸請求,此時,普通瀏覽器會正常返回登陸後的敏感信息,且敏感信息會進駐 bad.dd.com 內存空間。好不容易站點隔離把各個站點信息分開了,這由於跨域又在一塊兒了。咋整?CORB 來了。CORB 會在敏感信息到達 web apge 以前,將其攔截掉,如此,敏感信息既不會暴露於瀏覽器,也不會進駐內存空間,獲得了很好的保護。


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 值的一部分。以下圖,

Content-Type: MIME

內容嗅探技術(MIME sniffing)

內容嗅探技術是指 當響應頭沒有指明 MIME type 或 瀏覽器認爲指定類型有誤時,瀏覽器會對內容資源進行檢查並執行,來猜想內容的正確MIME類型。嗅探技術的實現細節,不一樣的瀏覽器在不一樣的場景下有不一樣的方式,本文不作詳述。詳細內容參見:www.keycdn.com/support/wha…

如何禁用 MIME sniffing 呢?

服務器在響應首部添加 X-Content-Type-Options: nosniff,用來告訴瀏覽器必定要相信 Content-Type 中指定的 MIME 類型,不要再使用內容嗅探技術探測響應內容類型。該方法僅對 <script><style> 有效。

官方解釋:developer.mozilla.org/en-US/docs/…

瀏覽器如何判斷響應內容是否須要 CORB 保護?

這多是本文最須要關心的內容了,到底什麼狀況下會出現 CORB 。在知足跨域標籤(如:<script><img>)請求的響應內容的 MIME typeHTML MIME typeXML MIME typeJSON MIME typetext/plain 時,如下三個條件任何一個知足,就享受 CORB 保護。(image/svg+xml 不在內,屬圖片類型)

  • 響應頭包含 X-Content-Type-Options: nosniff
  • 響應結果狀態碼是 206 Partial Contentdeveloper.mozilla.org/zh-CN/docs/…
  • 瀏覽器嗅探響應內容的 MIME 類型結果就是 json/xml/html

這種嗅探用於防止某些內容因被錯誤標記 MIME 類型 而被 CORB 阻斷不能正常響應返回,且該嗅探基於 Content-Type 進行,好比類型是 text/json,便只會對內容進行 json 類型檢查,而不會進行 xml 或 html 的檢查。

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 瀏覽器訪問 /file 時服務返回 data.txt 內容,儘管響應頭是 Content-Type: text/plain; charset=UTF-8,響應內容依舊能被識別爲 json。由此, text/plain 會做爲 json 的標記也是一種常見現象。若是跨域訪問 /file 就會出現 CORB,驗證結果以下圖,

跨域訪問

若是使用 script 跨域請求本就是 js 資源,但該資源卻被打上了錯誤的 Content-Type,還添加了 nosiniff,會發生什麼?

不少時候 script 文件被會打上 html xml json 這些 MIME 類型,若是 Chrome 瀏覽器直接 block,將相應內容置空,當前域下的網站便會 由於缺乏 js 執行內容而不能正常運行。爲避免這種狀況出現,Chrome 瀏覽器在決定是否保護響應內容前,會先判斷 script 的響應內容是不是受保護的 MIME 類型(html xml json )。若是檢測結果是,則啓動 CORB,若是沒法檢測會直接返回,不啓用 CORB。

對於跨域請求 js 資源,若是已經存在 nosniff 的狀況下,還把 js 資源設置成了其它類型(如:json),那麼一定觸發 CORB 保護機制,沒法返回 js 資源內容,若是此時本域站點恰好須要這個 js 資源,就 GG 了。至關於 錯誤的 MIME type 加上 X-Content-Type-Options: nosniff 會觸發 CORB ,即便資源真正的類型同跨域標籤一致。


爲了最佳安全策略,建議開發者

  1. 爲響應內容標記正確的 Content-Type
  2. 使用 X-Content-Type-Options: nosniff 禁止 MIME sniffing,如此,可讓瀏覽器不進行內容 MIME 類型嗅探,從而更簡單快速地保護資源或響應返回。

控制檯出現 CORB 提示時,不用擔憂,通常不會對頁面產生本質性的影響,能夠直接忽略。


Chrome 發生 CORB 保護時的提示和行爲驗證

雖然該部份內容屬於驗證類,但想了解一項知識點,僅僅簡單地閱讀是不夠的,實際操做試驗後才能得到更深的印象和理解。

環境準備

Chrome 版本: Chrome 73

Node 服務代碼 index.js

const express = require('express')
const path = require('path')
const app = express()
const port = process.argv[2] || 3002

app.get('/', function (req, res) {
  res.send('<p>hello world!</p>')
})

app.get('/data', function (req, res) {
  console.log('請求正常,只是瀏覽器將響應數據置空')
  res.json({greeting: 'hello chrome!'})
})

app.listen(port, () => console.log(`app is listening at localhost:${port}`))
複製代碼

配置 host

127.0.0.1 a.dd.com
127.0.0.1 c.dd.com
127.0.0.1 test.pp.com
複製代碼

實驗一:在test.pp.com中使用 img 標籤跨域請求 c.dd.com 的數據,數據 MIME 類型爲 json

  1. 執行 npm initnpm install 安裝服務依賴包
  2. 執行 node index.js 啓動服務
  3. 瀏覽器中訪問 test.pp.com:3002 並打開 開發者工具
  4. 開發者工具 Elements 中插入 <img src="http://c.dd.com:3002/data"/> (選中 body 元素,再按 F2 便可進入 html 編輯模式)

Elements 插入 img 標籤

  1. 查看控制檯 console 便可見,CORB 提示。

控制檯結果

  1. 刪掉 app.get('/data') 方法返回的數據 {greeting: 'hello chrome!'},即 將服務自己返回的數據自己置空,CORB 提示消失,但依舊看不到請求頭 和 響應結果。

實驗結果:1. 使用 `img` 跨域請求 json 類型的數據確實會出現 CORB;2. 當服務自己返回數據爲空時,CORB 提示會消失,但其行爲依然保持。

實驗二:在test.pp.com 中使用 script 跨域請求 c.dd.com 的數據,數據 MIME 類型爲 json

  1. 補回實驗一刪掉的代碼 {greeting: 'hello chrome!'},瀏覽器中訪問 test.pp.com 並 打開開發者工具。
  2. 在開發者工具 console 欄中執行下方代碼,便可插入 js 標籤併發送跨域請求。
s = document.createElement('script')
s.src = 'http://c.dd.com:3002/data'
document.head.appendChild(s)
複製代碼

效果如圖,

控制檯結果

img 表現一致,出現了 CORB 提示。清除{greeting: 'hello chrome!'},將服務返回數據置空,效果同 img 方式表現一致,CROB 提示消失。其它行爲也同 img

實驗結果:同 `img` 行爲效果如出一轍。

看了瀏覽器中 請求的響應 狀況,如今看看,兩次實驗的 請求執行 狀況,

實驗結果

能夠看到儘管產生了 CORB 保護,讓響應結果變爲空,也隱藏了請求頭,但服務請求自己始終正常接收請求並進行處理。由此,看到 CORB 後,通常能夠直接忽略該提示。

若是跨域請求 http://a.dd.com:3002/data 自己發生錯誤,則徹底無需 CORB 的保護,自己就已經不能正常返回了。所以,更不須要 CORB 的提示和行爲。

實驗三:在 test.pp.com 中跨域請求 c.dd.com 服務的 server.js 文件

本實驗旨在驗證 在某站點跨域請求 js 文件,而該 js 文件被設置了不一樣的 MIME 類型 和 nosniff 時,Chrome 是否會出現 CORB 。

第一步,server.js 文件 MIME 類型爲默認,不設置 nosniffc.dd.com服務代碼 index.js 中添加以下代碼,

代碼片斷一,

app.get('/file', function getState(req,res,next){
  res.sendfile(`${__dirname}/public/js/server.js`)
})
複製代碼

打開 Chrome test.pp.com 的開發者工具,並在開發者工具中執行以下代碼,跨域請求 server.js

代碼片斷二,

s = document.createElement('script')
s.src = 'http://c.dd.com:3002/file'
document.head.appendChild(s)
複製代碼

運行結果以下圖,

請求頭和響應頭
響應結果

如圖所示:真實請求頭被隱藏,Provisional headers are shown;響應頭可見;響應結果可見

第二步,設置 MIME 類型爲 json,即 Content-Type: application/json; charset=utf-8,不設置 nosniff。修改 index.js /file 部分代碼以下,

代碼片斷三,

app.get('/file', function getState(req,res,next){
  res.type('json')
  res.sendfile(`${__dirname}/public/js/server.js`)
})
複製代碼

再次執行本實驗 代碼片斷二,發現運行結果同第一步(即默認 MIME 類型)徹底同樣:真實請求頭被隱藏,Provisional headers are shown;響應頭可見;響應結果可見。

第三步,設置 MIME 類型爲 json,即 Content-Type: application/json; charset=utf-8,並添加 'X-Content-Type-Options': 'nosniff' 響應頭。(若是不理解該響應頭的含義,請再次閱讀文頂內容嗅探相關描述) 兩個響應頭加在一塊兒的意思是,明明本身是 js ,卻告訴瀏覽器 MIME 類型是 json,還非不讓瀏覽器使用嗅探技術修正 MIME 類型。 修改 index.js /file部分代碼以下。

代碼片斷四,

app.get('/file', function getState(req,res,next){
  res.type('json')
  res.set({
    'X-Content-Type-Options': 'nosniff'
  })
  res.sendfile(`${__dirname}/public/js/server.js`)
})
複製代碼

再次執行本實驗 代碼片斷二,運行結果以下圖,

控制檯提示
請求頭
響應結果

如圖所示: 跨域請求 js 文件時,若是沒有設置 nosniff,甭管 MIME 類型設置了什麼,都只是請求頭不顯示,響應頭和響應結果正常顯示。若是設置了 nosniff 且 MIME 類型不是 js,則會觸發 CORB 保護,跨域 js 沒法正常加載。

所以,若是做爲跨域站點 c.dd.com 和 本域站點 test.pp.com 合做時,若是爲了 減小 MIME 類型嗅探時間 加上了 nosniff 請求頭,同時,需務必保證設置的 MIME 類型同 js 文件一致!不然 本域站點 沒法拿到 跨域站點 的 js 資源數據!

----------------- 關於 CORB , Chrome 表現和行爲驗證結束 -------------------

原文地址:www.yaoyanhuo.com/blog/corb

參考內容

做者博客主頁:www.yaoyanhuo.com

相關文章
相關標籤/搜索