您現在的位置是:首頁 > 單機遊戲首頁單機遊戲

如何在瀏覽器中儲存會話令牌

簡介html原始碼:https:github

如何開啟令牌

最近有一條關於OWASP ASVS的提議變更的推文引發了一場激烈的爭論,並且挑戰了我在構建和設計單個頁面應用程式時對儲存會話令牌的不同策略的理解。雖然以前有很多關於這方面的文章,但我也有了自己的研究。

為此,我決定製作一系列的“概念驗證”單頁應用程式來說明每一個不同的策略。我還希望這些完全是準PoC,實際上,大多數SPA可能會利用現有的框架和庫,但我發現這些抽象使抓取概念變得困難,因此我沒有使用任何JS框架,只是使用簡單,易於遵循的原始JavaScript。

為了幫助說明關於ASVS的爭論,我還在每個頁面中添加了一個簡單的“XSS”,並將展示攻擊者控制的JS在這些頁面中能夠產生的影響。

我的“app”由兩個API組成:

/api/login:這會生成並返回一個cookie或JSON資料的“會話令牌”,沒有使用者名稱/密碼。

/api/echo:這個端點簡單地用它收到的任何令牌進行響應,如果沒有傳送令牌,則表示“未授權”,假設這只是一個經過身份驗證的端點。

因此,開啟一個新的標籤頁到https://tokenstorage。ropnop。dev並繼續操作!

Cookies

PoC頁面:https://tokenstorage。ropnop。dev/cookie。html

原始碼:https://github。com/ropnop/tokenstorage/blob/master/cookie。html

這是一種“經典”方法,早於單頁面應用程式。在這個場景中,在POST ‘ing到/api/login之後,端點用Set-Cookie標頭中生成的會話令牌進行響應。你可以透過點選“Login and get a new token”來驗證這一點,並在Developer Tools中檢視響應:

如何在瀏覽器中儲存會話令牌

在本例中,我們將會話令牌的“所有權”委託給瀏覽器。瀏覽器識別設定的值,並將其儲存在“cookie jar”中,這也可以在開發人員工具中檢視:

如何在瀏覽器中儲存會話令牌

我們的客戶端程式碼不需要對/ api / login中的響應主體進行任何操作,由於瀏覽器已為我們儲存了cookie,因此無需捕獲或記住任何內容。只要該cookie值存在並且沒有過期,瀏覽器就會自動將該值傳送到與域和路徑集匹配的任何端點。從開發的角度來看,這是很棒的,我們甚至不需要考慮自己處理身份驗證,瀏覽器將僅傳送任何匹配的cookie,並且可以檢查伺服器端,但這也會帶來一些不良的意想不到的後果(例如CSRF)。

在我們的客戶端程式碼中,我們根本不需要做任何特殊的事情來發送經過身份驗證的請求,瀏覽器將為我們傳送cookie(如果存在的話)。所以我的原始JS API呼叫看起來像這樣:

如何在瀏覽器中儲存會話令牌

我只需要在資源上使用fetch,它就能工作。你可以透過在頁面中點選 ‘Make an “authenticated” request’ 來驗證這一點,並看到伺服器響應傳送的cookie值。你可以透過檢視開發人員工具中的請求標頭來驗證Cookie是“自動”傳送的:

如何在瀏覽器中儲存會話令牌

XSS的影響

因為這個cookie是用HttpOnly設定的,所以任何客戶端JavaScript程式碼都無法訪問它。如果攻擊者在我們的SPA上獲得了XSS,那麼攻擊者就無法讀取cookie值。你可以透過在XSS框中輸入alert(document。cookie)來進行嘗試,以確保無法讀取或顯示任何值:

如何在瀏覽器中儲存會話令牌

然而,重要的是要記住,即使攻擊者不能讀取cookie值,他或她仍然可以使用cookie值。因為瀏覽器將cookie與每個請求一起傳送到匹配的域,如果我們的XSS有效載荷是fetch(“/api/echo”),那麼cookie將被自動傳送,攻擊者可以讀取響應。

Cookies的特點

優點

容易實現,無需自定義客戶端(瀏覽器會為我們處理身份驗證);

如果設定HttpOnly,則無法從JS訪問;

缺點

自動傳送,可能會導致意外後果(CSRF);

不能跨域傳送;

永續性

在新的頁面、標籤、重新整理等情況下仍然存在;

持續直到故意刪除或過期,透過Cookie MaxAge或Expires屬性從伺服器控制;

本地儲存

PoC頁面:https://tokenstorage。ropnop。dev/localStorage。html;

原始碼:https://github。com/ropnop/tokenstorage/blob/master/localStorage。html;

在這個示例和下面的示例中,伺服器以JSON主體的形式響應會話令牌,這意味著由我們(客戶機)來管理它。一種方法是使用瀏覽器的LocalStorage API,這是範圍限定於源的永續性儲存。

在客戶端程式碼中,令牌響應在點選“Login”按鈕(下面第11行)後儲存到localStorage:

如何在瀏覽器中儲存會話令牌

現在已在localStorage中設定了令牌,我們可以在開發人員工具中進行驗證:

如何在瀏覽器中儲存會話令牌

將令牌儲存在本地儲存中後,令牌將再次由客戶端傳送(通常在“授權”標頭中)。如果存在,我們必須從localstorage中獲取它(第3-4行),然後使用我們的請求傳送自定義標頭(第6行):

如何在瀏覽器中儲存會話令牌

與cookie方法相反,我們必須明確地在客戶端程式碼中傳送自定義標頭。

不幸的是,LocalStorage不提供XSS保護,由於該值需要由JavaScript讀取,因此任何在同一來源(即XSS)執行的“惡意” JavaScript都將對本地儲存中的所有內容進行完全讀取/寫入。透過執行alert(window。localStorage。token)自己進行驗證:

如何在瀏覽器中儲存會話令牌

本地儲存的特點

持續重新整理/關閉頁面;

作用域為原點,而不是域;

從不自動傳送到任何地方(CSRF不可能);

透過惡意JS (XSS)輕鬆竊取

永續性

持續到故意刪除為止;

會話儲存

PoC頁面:https://tokenstorage。ropnop。dev/sessionStorage。html;

原始碼:https://github。com/ropnop/tokenstorage/blob/master/sessionStorage。html;

這幾乎與LocalStorage相同,但有一個重要的區別:SessionStorage不能跨瀏覽上下文持久儲存,這意味著如果關閉頁面,它將被刪除。

設定令牌幾乎與本地儲存相同,我們只是在檢索令牌後使用其他API儲存令牌:

如何在瀏覽器中儲存會話令牌

設定完成後,我們可以再次在開發人員工具中對其進行驗證:

如何在瀏覽器中儲存會話令牌

要傳送經過身份驗證的請求,我們再次必須從SessionStorage檢索它並將其作為自定義標頭髮送:

如何在瀏覽器中儲存會話令牌

同樣,LocalStorage和SessionStorage之間的唯一區別是永續性。如果關閉視窗或開啟一個新視窗,則令牌值將從SessionStorage中消失,而與LocalStorage相反。

XSS影響

就像LocalStorage一樣,SessionStorage提供針對XSS的零保護,因為它旨在透過JavaScript進行讀寫。你可以透過在PoC頁面中執行alert(window。sessionStorage。token)進行驗證:

如何在瀏覽器中儲存會話令牌

會話儲存特點

從未自動傳送到任何地方(CSRF impossible);

透過惡意JS (XSS)輕鬆竊取;

僅適用於當前瀏覽上下文

是否在重新整理過程中持續存在

頁面關閉或在新頁面/標籤上丟失

全域性變數

PoC頁面:https://tokenstorage。ropnop。dev/globalVar。html;

原始碼:https://github。com/ropnop/tokenstorage/blob/master/globalVar。html;

在接下來的幾個示例中,我們將完全不使用任何瀏覽器儲存,而僅依靠執行中的客戶端JavaScript來儲存、設定和檢索會話令牌。

實現此目的的最簡單方法是僅使用頂級全域性變數,我們需要的任何函式中的任何正在執行的客戶端程式碼都可以使用此變數。

為了演示,我在視窗上設定了一個頂級變數token,並在從登入API中檢索到令牌時進行了設定:

如何在瀏覽器中儲存會話令牌

要將其設定為Authorization標頭,我只需要引用它的變數名:

如何在瀏覽器中儲存會話令牌

這很容易實現,並且變數在需要時可用。這將無法在頁面重新整理或新的瀏覽上下文中保留,當JS重新載入時,這個變數就會被刪除。

由於令牌只是一個JavaScript變數,所以如果攻擊者獲得了惡意的JS執行,那麼讀取該值是很容易的。他或她唯一需要做的就是透過檢視客戶端程式碼找出變數的名稱,為了驗證你自己,試著在XSS框中輸入alert(window。token)或alert(token):

如何在瀏覽器中儲存會話令牌

全域性變數特點

容易實現;

令牌值只存在於“記憶體中”;

可以跨域傳送;

容易被惡意JS (XSS)竊取;

在頁面重新整理或新建頁面時丟失

閉包變數

PoC頁面:https://tokenstorage。ropnop。dev/privateVar。html;

原始碼:https://github。com/ropnop/tokenstorage/blob/master/privateVar。html;

現在,我們終於可以找到我認為是在瀏覽器中儲存會話令牌的最安全方法。在此示例中,我們再次將會話令牌值僅保留在“記憶體中”,但對其進行保護,以便可以使用其值,但實際上從未從任何其他JavaScript讀取過,可以透過將其儲存在閉包內部來實現此目的(如果有幫助,可以將其視為類似於類內的私有變數)。

從架構上講,我們唯一需要會話令牌的是傳送HTTP請求,因此我們可以設計閉包來公開一個fetch函式,該函式會自動附加令牌值。我們需要公開的另一件事是設定令牌的方法。

如何在瀏覽器中儲存會話令牌

這個名為authModule的閉包只公開了兩個函式:setToken和fetch。在設定標記值之後,就不可能再次讀取它了。這是關閉。fetch函式模模擬實的fetch函式,但是如果目標源與白名單匹配,它將附加授權頭。這一點非常重要,因為如果你沒有進行此檢查,此模組將向任何域傳送授權頭,而攻擊者可能透過XSS將敏感令牌傳送給自己來濫用此訊息。

在登入之前,我們現在例項化一個新的authModule(1),並在檢索令牌時將其設定為(13):

如何在瀏覽器中儲存會話令牌

現在,每當我們希望傳送經過身份驗證的請求時,我們都可以使用auth。fetch而不是普通提取(我添加了一個自定義標頭,只是為了演示它們也被髮送了):

如何在瀏覽器中儲存會話令牌

在這種情況下,XSS的影響非常小,就像HttpOnly cookie一樣,令牌本身的值不可能使用JavaScript提取。攻擊者可以使用身份驗證。獲取其XSS有效載荷中的內容,但它們只能將授權標頭髮送到白名單源。隨時嘗試透過XSS檢索令牌值,如果有人可以找到一種訪問令牌的方法。

如何在瀏覽器中儲存會話令牌

@coffeetocode演示了一個出色的XSS有效載荷,它覆蓋了正常的獲取操作以竊取授權cookie。我已經更新了程式碼,以在閉包中包含受保護的fetch副本:

閉包特點

令牌值不受其他JS程式碼的影響;

對令牌在何處/何時被髮送的準確控制;

實現稍微複雜一些

Service Worker

PoC頁面: https://tokenstorage。ropnop。dev/serviceWorker。html;

原始碼:

https://github。com/ropnop/tokenstorage/blob/master/serviceWorker。html;

https://github。com/ropnop/tokenstorage/blob/master/js/serviceWorker。js;

我要討論的最後一種方法可能是最複雜的,但在我看來是在瀏覽器中處理會話令牌最酷的方法,那就是透過使用Service Worker。在此之前,我沒有太多使用Service Worker的經驗,但它們是標準Web API的強大補充。Service Worker本質上是在自己的上下文中執行的瀏覽器代理伺服器中,並且在重新整理和新頁面載入之間持久存在。我們可以使用一個Service Worker來為我們記住會話令牌,然後為任何需要它的網路資源傳送會話令牌。

首先,我們需要在希望Service Worker監視的頁面上註冊Service Worker。在我們的SPA中,如下所示:

如何在瀏覽器中儲存會話令牌

在第4行中,我將範圍限制為僅此特定頁面,但在實踐中,我們可以使用/代替在每個頁面上執行此操作。

實際的ServiceWorker程式碼有點複雜,但是核心函式應該類似於上面的閉包邏輯。有一組列入目的地清單的白名單,並且我還為路徑實現了正則表示式:

如何在瀏覽器中儲存會話令牌

我們需要使用postMessage將令牌值傳送給Service Worker,而不是像在閉包示例中那樣建立公開的函式來設定令牌值。由於Service Worker在其自身的上下文和來源中執行,因此它充當了一種“ RPC”的角色。因此,我們設定了一個偵聽器來接收令牌值並進行設定:

如何在瀏覽器中儲存會話令牌

現在,回到我們的SPA,我們將登入後收到的token值傳送給服務人員:

如何在瀏覽器中儲存會話令牌

現在Service Worker擁有了標記值並將記住它。

為了使用令牌,我們可以使用Service Worker的魔力來攔截每個fetch呼叫,並確定是否需要新增令牌值。我們透過設定一個事件監聽器來獲取Service Worker:

如何在瀏覽器中儲存會話令牌

執行此事件偵聽器後,如果將目的地列入白名單,我們在SPA中進行的所有正常提取操作都會自動傳送秘密令牌。因此,我們可以使用正常的提取操作,而不必擔心身份驗證(有點像可以使用cookie):

繼續並在PoC頁面上嘗試一下。關閉頁面並重新整理,然後嘗試傳送經過身份驗證的請求而不請求新令牌,服務人員會記住先前的令牌!

與閉包類似,這裡不存在XSS影響。據我所知,SPA中的XSS無法訪問或修改Service Worker內部的值。攻擊者唯一能做的就是透過postMessage設定一個假的令牌值,或者使用fetch從受害者的瀏覽器傳送一個經過身份驗證的請求。

如何在瀏覽器中儲存會話令牌

Service Worker的特點

Token值只存在於service worker中;

準確控制令牌在何處/何時被髮送;

令牌會自動新增,沒有特殊的客戶端程式碼可傳送已驗證的請求

實現起來要複雜得多

持續重新整理和載入新頁面;

持續到Service Worker取消註冊/更新為止;

總結

我希望這有助於說明在瀏覽器中儲存和傳送秘密令牌的不同方法背後的概念,本文的目標是透過儘可能簡單的PoC演示每個選項的示例,然後評估每個選項的XSS影響和永續性。

Top