使用SRI保護你的網站免受第三方CDN惡意攻擊

CarterLi發表於2019-02-16

出於速度和降低伺服器負載考慮,有時候我們會選擇使用 CDN 載入第三方靜態資源。對於一些熱門的第三方庫,在使用者開啟你的網頁之前就很有可能在瀏覽別的網站時被瀏覽器快取下來,這樣就可以極大的提升網頁載入速度。

然而使用 CDN 也提高了網站的安全風險:第三方靜態資源放在第三方伺服器上,CDN 的擁有者有沒有可能偷偷的篡改這些檔案,加入惡意程式碼呢?或者 CDN 伺服器遭受了黑客攻擊,整個檔案被替換掉。雖然可能性不高,但不是零。JavaScript 對於當前瀏覽器頁面有完全控制權,他們不僅僅能獲取到頁面上的任何內容,還能抓取使用者輸入的一些諸如密碼之類的機密資訊,還能獲取到儲存到 Cookie 中的登入票據等等內容,這就是所謂的 XSS 攻擊。

我們需要一種機制確保從 CDN 下載的檔案未被惡意篡改。某些下載網站就提供下載檔案的 MD5 或 SHA1 碼用於檢查所下載檔案的完整性,網頁中有沒有類似的機制呢?

什麼是 SRI

子資源完整性 Subresource Integrity 簡稱 SRI 是一種安全機制,它用於讓瀏覽器檢查所下載的來自第三方的資源(例如 CDN)未被惡意篡改。它使用雜湊值檢查確保第三方資源的完整性。只要開發者提供了被需下載資源的雜湊值,瀏覽器就可以檢查實際下載的檔案是否與預期的雜湊值匹配。

使用 SRI

只需給 scriptstyle 標籤新增 integrity 屬性即可。例如:

  • JavaScript
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha384-xBuQ/xzmlsLoJpyjoggmTEz8OWUFM0/RC5BsqQBDX2v5cMvDHcMakNTNrHIW2I5f" crossorigin="anonymous"></script>
  • CSS
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css" integrity="sha384-7tIwW4quYS2+TZCwuAPnUY+dRqg28ylzlIoVXAwpfiTs+CMKsAOSsWYQ96c/ZnV+" crossorigin="anonymous">

integrity 屬性值以 shaXXX- 開頭,表示後面的雜湊值使用的雜湊演算法,目前只允許 sha256sha384sha512 這三種雜湊演算法,以 sha384 比較多見。後面跟對應的雜湊值即可。

值得注意的是,因為啟用 SRI 需要獲取所下載檔案的內容進行計算,所以需要 CDN 伺服器啟用跨域資源訪問(CORS)支援,即返回 Access-Control-Allow-Origin: * 頭。客戶端需要使用跨域的形式載入指定檔案,即新增 crossorigin="anonymous" 屬性。就我所知,目前國內相對常用的免費 CDN bootcdn 已經支援 CORS,百度靜態 CDN 還不支援。

瀏覽器如何處理 SRI

  1. 當瀏覽器遇到一個帶有 integrityscriptstyle 標籤,在執行其中的 JS 指令碼或應用其中的 CSS 樣式之前,瀏覽器會首先計算所下載檔案的內容的雜湊值是否與 integrity 屬性給定的值相同。
  2. 如果計算結果與給定值不匹配,瀏覽器會拒絕執行指令碼內容,並報出一個網路錯誤,類似如下結果:
Failed to find a valid digest in the `integrity` attribute for resource `https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css` with computed SHA-256 integrity `VbcxqgMGQYm3q8qZMd63uETHXXZkqs7ME1bEvAY1xK8=`. The resource has been blocked.

如何計算雜湊值

這是 SRI 標準文件提供的例子

$ echo -n "alert(`Hello, world.`);" | openssl dgst -sha384 -binary | openssl base64 -A

使用了 OpenSSL 這個 *nix 中通常都包含的工具計算雜湊值。其中 alert(`Hello, world.`); 是檔案內容,你也可以用 cat Filename.js 直接讀取某個檔案。

輸出 H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO,在此基礎上新增字首 sha384- 就可以了。

網上也有現成的 SRI 雜湊值生成器,方便好用:https://srihash.org/

CSP 與 SRI

你可以使用 內容安全政策CSP)強制要求當前頁面所有指令碼載入標籤啟用 SRI。例如

Content-Security-Policy: require-sri-for script;

強制要求所有 script 標籤啟用 SRI,瀏覽器會拒絕載入未啟用 SRI 的 script 標籤。

對應的還有 CSS 版本:

Content-Security-Policy: require-sri-for style;

你也可以同時啟用兩者。

錯誤恢復

使用 CDN 時別忘了當嘗試從 CDN 載入檔案失敗後載入本地版本:

<script src="https://code.jquery.com/jquery-3.2.1.min.js"
        integrity="sha384-xBuQ/xzmlsLoJpyjoggmTEz8OWUFM0/RC5BsqQBDX2v5cMvDHcMakNTNrHIW2I5f"
        crossorigin="anonymous"></script>
<script>if (!window.jQuery) document.write(`<script src="/jquery-3.2.1.min.js"></script>`)</script>

相關文章