了解子資源完整性

已發表: 2022-03-10
快速總結↬你添加到網站的每一點 JavaScript 都是黑客的潛在途徑。 如果該 JavaScript 由其他人託管,例如在公共 CDN 上,則情況更是如此。 子資源完整性是一種瀏覽器功能,您可以使用它來確保所使用的代碼正是您想要的。

如果您曾經使用過 CDN 託管版本的 JavaScript 庫,您可能已經註意到 script 標籤上的一個奇怪的integrity屬性。 此屬性包含看似無窮無盡的字母數字垃圾,您可能會在尋求更清晰的代碼時將其刪除。

所有這些垃圾實際上都是一個非常有用的安全功能,稱為子資源完整性 (SRI),它可以幫助保護您的網站免受某些類型的黑客攻擊和危害。 在本文中,我們將了解 SRI 是什麼,它如何幫助保護您,以及如何在您自己的項目中開始使用它,而不僅僅是用於託管在 CDN 上的文件。

一點歷史

早在 JavaScript 是 HTML 和 CSS 的表親的時代,我們不需要過多考慮如何將我們的腳本用作我們網站的攻擊媒介。 大多數站點都託管在我們自己的託管基礎設施某處的單個物理服務器上,當涉及到安全最佳實踐時,它是我們考慮防禦的服務器。

隨著瀏覽器的功能越來越強大,網絡連接越來越胖,我們開始使用越來越多的 JavaScript,最終,可重用的 JavaScript 庫開始湧現。 在早期,像 script.aculo.us、Prototype 和 jQuery 這樣的庫開始在希望在頁面中添加更多交互性的開發人員中獲得採用。

隨著這些添加的庫和後續插件的增加,頁面重量也隨之增加,不久之後我們開始認真考慮前端性能。 內容交付網絡 (CDN) 之類的資源,以前是大公司的儲備,現在已經成為日常人們構建時髦網站的普遍方式。

跳躍後更多! 繼續往下看↓

一路上,一些明亮的火花注意到網站都在請求他們自己的通用庫副本——比如最新的 jQuery——如果這些庫的通用 CDN 版本可以被每個站點使用,那麼用戶就不會t 需要繼續下載相同的文件。 他們會在第一個站點使用該文件時受到打擊,但隨後它將位於本地瀏覽器緩存中,並且可以跳過每個後續站點的下載。 天才!

這就是為什麼您會使用jsdelivr.com類的 URL 看到您喜歡的庫的 CDN 鏈接的原因——他們使用通用 CDN 來託管文件,以便他們的用戶看到性能優勢。

會出什麼問題?

這仍然是一種很好、實用的工作方式,但它確實引入了潛在的攻擊媒介。 假設現在是 2012 年,每個人都在使用全新的 jQuery 1.8。 回到傳統的做事方式,每個人都可以將自己的 jQuery 1.8 文件作為自己網站的一部分託管在自己的服務器上。

如果你是某種邪惡的演員——比如某種基於 jQuery 的 Hamburglar——並且想出了一種偷偷摸摸的方法來破解庫以獲得自己的邪惡利益,那麼你必須單獨針對每個網站並破壞他們的服務器才能擁有任何影響。 這是一個很大的努力。

但現在情況並非如此,因為每個人都在使用從通用 CDN 加載的 jQuery。 當我說每個人時,我並不是指數百個網頁。 我的意思是數百萬個網頁。 突然間,這個文件成為了我們黑幕黑客的一個非常有吸引力的目標。 如果他們可以破壞該文件,他們可以非常快速地在全球數百萬個網頁中運行代碼。

該代碼是什麼並不重要。 可能是破壞頁面的惡作劇,可能是竊取密碼的代碼,可能是挖掘加密貨幣的代碼,也可能是在網絡上跟踪您並製作營銷資料的偷偷摸摸的跟踪器。 重要的是,開發人員添加到頁面的無辜文件已被更改,您現在有一些惡意 JavaScript 作為您網站的一部分運行。 這是個大問題。

輸入子資源完整性

SRI 不是回滾時鐘並放棄使用代碼的有用方式,而是一種在頂部增加簡單安全級別的解決方案。 SRI 和integrity屬性所做的是確保您鏈接到頁面的文件永遠不會更改。 如果它確實改變了,那麼瀏覽器將拒絕它。

檢查代碼沒有改變是計算機科學中一個非常古老的問題,幸運的是它有一些非常成熟的解決方案。 SRI 在採用最簡單的文件散列方面做得很好。

文件散列是獲取文件並通過一種算法運行它的過程,該算法將其簡化為短字符串表示,稱為散列或校驗和。 無需深入研究,該過程要么是可重複的,要么是可逆的,以至於如果您要給其他人一個文件以及哈希值,他們將能夠運行相同的算法來檢查兩者是否匹配。 如果文件更改或哈希更改,則不再匹配,您知道有問題並且應該不信任該文件。

使用 SRI 時,您的網頁保存哈希,服務器(CDN 或任何地方)保存文件。 瀏覽器下載文件,然後快速計算以確保它與integrity屬性中的哈希匹配。 如果匹配,則使用該文件,如果不匹配,則阻止該文件。

試一試

如果我今天去getbootstrap.com獲取指向 Bootstrap 版本的 CDN 鏈接,我會得到一個如下所示的標籤:

 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

您可以看到src屬性與我們習慣的一樣,而integrity屬性包含我們現在知道的哈希值。

哈希實際上分為兩部分。 第一個是聲明要使用哪種散列算法的前綴。 在這種情況下,它是sha384 。 後面是一個破折號,然後是哈希本身,用base64編碼。

您可能熟悉base64作為一種將內聯文件(如圖像)編碼到頁面中的方式。 這不是一個加密過程——它只是一種快速便捷的方式,以一種可以巧妙地轉換為 ASCII 的方式對潛在的混亂數據進行編碼。 這就是為什麼它在網絡上被大量使用的原因。

看到這個,瀏覽器會下載bootstrap.min.js 。 在執行它之前,它會對哈希進行base64解碼,然後使用sha384哈希算法來確認哈希是否與它剛剛下載的文件匹配。 如果匹配,則執行該文件。

我可以通過將該標籤放在頁面中進行測試,然後在瀏覽器工具中翻到網絡選項卡以查看文件是否已加載。

網絡選項卡
(大預覽)

我可以看到bootstrap.min.js (以及它需要的 jQuery 文件)已成功加載。

讓我們看看如果我將哈希更新為我知道不正確的東西會發生什麼。

 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-SmashingMagazineIsCoolForCats" crossorigin="anonymous"></script> 
哈希
(大預覽)

如您所見,在我的頁面中指定的哈希不再與文件匹配,因此文件被阻止。

在您自己的項目中使用 SRI

在 CDN 上為庫提供此功能非常棒,如果您看到使用具有integrity屬性的嵌入式文件的選項,那麼您絕對應該支持該選項。 但它不僅限於 CDN 上的大型項目,您可以自己將其用於自己的站點。

想像一個黑客設法訪問您網站上的幾個文件的場景一點也不牽強。 我想我們中的大多數人都見過一個客戶、同事或朋友,他們在某個時候的 WordPress 網站受到了他們甚至沒有意識到的大量令人討厭的垃圾的影響。

SRI 也可以保護您免受此影響。 如果您為自己的文件生成完整性哈希,那麼您可以讓您的站點拒絕任何更改,就像對遠程託管文件一樣。

生成哈希

如您所料,您可以在計算機終端上運行一些命令來為文件生成哈希。 這個如何做到這一點的示例來自 MDN 子資源完整性頁面:

 cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A

這是獲取FILENAME.js的內容並將其作為輸入傳遞給openssl以使用sha384創建摘要,然後將其作為輸入傳遞給另一個openssl命令以對結果進行base64編碼。 這不僅複雜且晦澀難懂,而且也不是您每次 JavaScript 文件更改時都希望手動執行的操作。

更有用的是,您會希望以某種方式將其集成到您網站的構建過程中,並且正如您想像的那樣,那裡有很多現成的選項。 確切的實現將根據您的項目而大不相同,但這裡有一些構建塊。

如果您使用 Gulp 構建您的站點,則 gulp-sri 將輸出一個 JSON 文件,其中包含您的文件列表及其哈希值。 然後,您可以在您的站點中使用它。 例如,對於動態呈現的站點,您可以創建一個模板插件來讀取該文件並在需要時將哈希添加到您的模板中。

如果您仍在使用 Gulp,但有一個靜態站點(或靜態生成的站點),您可能會使用 gulp-sri-hash,它實際上會運行您的 HTML 頁面並修改頁面以在需要的地方添加哈希,這非常方便。

如果您使用的是 Webpack,網頁子資源完整性在真正的 Webpack 風格中比任何人預期的要復雜,但似乎確實有效。

對於那些使用 Handlebars 模板引擎的人來說,似乎有一些選項可供您使用,如果您的構建過程只是基本的 JavaScript,那麼那裡也有簡單的解決方案。

如果您使用的是 WordPress 之類的 CMS,我發現了一個看起來很容易的插件,儘管我自己沒有嘗試過。 使用 SRI 或 Sub Resource Integrity 搜索您自己選擇的平台可能會為您指明正確的方向。

你基本上想在你的 JavaScript 文件被縮小掛鉤你的散列,然後以某種方式使該散列可用於系統的任何部分輸出<script>標記。 Web 平台的一大優點是它在技術上如此多樣化,但遺憾的是,我無法為您提供良好的實施說明!

其他注意事項

在本文中,我談了很多關於 JavaScript 文件的內容,因為這確實是防禦黑客攻擊最有意義的地方。 SRI 也適用於 CSS,因此您可以以完全相同的方式使用它。 惡意 CSS 的風險要低得多,但破壞網站的可能性仍然存在,而且誰知道哪些瀏覽器錯誤也可能導致 CSS 無意中將您的網站暴露給黑客。 所以在那裡也可以使用 SRI。

您可以做的另一件有趣的事情是使用內容安全策略來指定頁面上的任何腳本(或樣式)必須使用 SRI,當然 SRI 必須驗證。

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

這是一種確保始終使用 SRI 的方法,這在由多個團隊成員工作的站點上可能很有用,這些成員可能會或可能不會完全了解如何做事。 再次,閱讀更多有關此內容的好地方是始終很棒的 MDN 文檔,用於 Subresource Integrity。

最後值得一提的是瀏覽器對 SRI 的支持。 現代瀏覽器的支持範圍很廣,主要的例外是 Internet Explorer。 然而,由於規範已經實現了向後兼容的方式,它可以安全地立即使用。 了解integrity屬性的瀏覽器將使用散列並檢查完整性,而較舊的瀏覽器將一如既往地繼續工作並繼續工作。 當然,您不會在那些較舊的瀏覽器中獲得額外的保護,但您會在提供支持的瀏覽器中獲得。

結論

我們不僅看到了integrity屬性中那些奇怪的哈希值的作用,還看到了我們如何使用它們來防禦我們網站上的某些類型的攻擊。 當然,沒有什麼靈丹妙藥可以保護我們的網站免受各種類型的攻擊,但子資源完整性是鏈條中非常有用的工具。

利用安全漏洞通常是將多個小塊排列起來。 如果 A 到位,並且您可以使 B 發生,那麼 C 中的錯誤使 D 成為可能。 SRI 之類的瀏覽器功能為我們提供了一種很好的方法,可以將事情進一步捆綁,並有可能打破該鏈條並防止黑客獲得他們想要的東西。 更重要的是,如果您可以將它集成到您​​的構建過程或 CMS 中,那麼您應該能夠設置一次然後忘記它,它不會給您帶來日常的不便。

因此,我真的建議認真研究 Subresource Integrity 並儘可能在您的站點上實施它。