出于速度和降低服務(wù)器負載考慮艺配,有時候我們會選擇使用 CDN 加載第三方靜態(tài)資源。對于一些熱門的第三方庫惋砂,在用戶打開你的網(wǎng)頁之前就很有可能在瀏覽別的網(wǎng)站時被瀏覽器緩存下來妒挎,這樣就可以極大的提升網(wǎng)頁加載速度。
然而使用 CDN 也提高了網(wǎng)站的安全風(fēng)險:第三方靜態(tài)資源放在第三方服務(wù)器上西饵,CDN 的擁有者有沒有可能偷偷的篡改這些文件酝掩,加入惡意代碼呢?或者 CDN 服務(wù)器遭受了黑客攻擊眷柔,整個文件被替換掉期虾。雖然可能性不高原朝,但不是零。JavaScript 對于當(dāng)前瀏覽器頁面有完全控制權(quán)镶苞,他們不僅僅能獲取到頁面上的任何內(nèi)容喳坠,還能抓取用戶輸入的一些諸如密碼之類的機密信息,還能獲取到保存到 Cookie 中的登錄票據(jù)等等內(nèi)容茂蚓,這就是所謂的 XSS 攻擊壕鹉。
我們需要一種機制確保從 CDN 下載的文件未被惡意篡改。某些下載網(wǎng)站就提供下載文件的 MD5 或 SHA1 碼用于檢查所下載文件的完整性聋涨,網(wǎng)頁中有沒有類似的機制呢晾浴?
什么是 SRI
子資源完整性?Subresource Integrity?簡稱 SRI 是一種安全機制,它用于讓瀏覽器檢查所下載的來自第三方的資源(例如 CDN)未被惡意篡改牍白。它使用哈希值檢查確保第三方資源的完整性脊凰。只要開發(fā)者提供了被需下載資源的哈希值,瀏覽器就可以檢查實際下載的文件是否與預(yù)期的哈希值匹配茂腥。
使用 SRI
只需給?script?或?style?標簽添加?integrity?屬性即可狸涌。例如:
integrity?屬性值以?shaXXX-?開頭,表示后面的哈希值使用的哈希算法最岗,目前只允許?sha256、sha384?或?sha512?這三種哈希算法仑性,以?sha384?比較多見。后面跟對應(yīng)的哈希值即可歼捐。
值得注意的是晨汹,因為啟用?SRI?需要獲取所下載文件的內(nèi)容進行計算,所以需要 CDN 服務(wù)器啟用跨域資源訪問(CORS)支持剥扣,即返回?Access-Control-Allow-Origin: *?頭铝穷。客戶端需要使用跨域的形式加載指定文件曙聂,即添加?crossorigin="anonymous"?屬性。就我所知断国,目前國內(nèi)相對常用的免費 CDN?bootcdn?已經(jīng)支持 CORS,百度靜態(tài) CDN 還不支持霞捡。
瀏覽器如何處理 SRI
當(dāng)瀏覽器遇到一個帶有?integrity?的?script?或?style?標簽碧信,在執(zhí)行其中的 JS 腳本或應(yīng)用其中的 CSS 樣式之前输涕,瀏覽器會首先計算所下載文件的內(nèi)容的哈希值是否與?integrity?屬性給定的值相同慨畸。
如果計算結(jié)果與給定值不匹配寸士,瀏覽器會拒絕執(zhí)行腳本內(nèi)容,并報出一個網(wǎng)絡(luò)錯誤乃正,類似如下結(jié)果:
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.');?是文件內(nèi)容凡人,你也可以用?cat Filename.js?直接讀取某個文件。
輸出?H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO传睹,在此基礎(chǔ)上添加前綴?sha384-?就可以了欧啤。
網(wǎng)上也有現(xiàn)成的 SRI 哈希值生成器启上,方便好用:https://srihash.org/
CSP 與 SRI
你可以使用?內(nèi)容安全政策?(CSP)強制要求當(dāng)前頁面所有腳本加載標簽啟用 SRI。例如
Content-Security-Policy:?require-sri-for?script;
強制要求所有?script?標簽啟用 SRI倒慧,瀏覽器會拒絕加載未啟用 SRI 的 script 標簽迫靖。
對應(yīng)的還有 CSS 版本:
Content-Security-Policy:?require-sri-for?style;
你也可以同時啟用兩者。
錯誤恢復(fù)
使用 CDN 時別忘了當(dāng)嘗試從 CDN 加載文件失敗后加載本地版本:
原鏈接:https://segmentfault.com/a/1190000011337230