30 分鐘理解 CORB 是什麼

HaoliangWu發表於2018-08-23

寫在前面

前些日子在除錯 bug 的時候,偶然發現這麼一個警告:

Cross-Origin Read Blocking (CORB) blocked cross-origin response www.chromium.org/ with MIME type text/html. See www.chromestatus.com/feature/562… 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 的文件也很詳細,只是有些長,需要耐心慢慢讀完。

以上,如有錯誤,還望指出,大神輕噴。

參考連結

相關文章