不要使用 JWT 進行會話管理


英文原文地址:Stop using JWT for sessions

最近我發現越來越多的人推薦使用 JWT 來在 Web 應用中管理會話(Session),這是一個非常非常糟糕的主意,在這篇文章中我會詳細地解釋為什么“不要使用 JWT 進行會話管理”。

為了方便敘述的同時避免混淆,我需要先定義一些概念:

  • 無狀態 JWT(Stateless JWT):一個包含了所有會話相關數據的 JWT token。
  • 有狀態 JWT(Stateful JWT):一個僅包含了會話相關數據 ID 的 JWT token,真正的會話相關數據被存儲在服務端。
  • Session token/cookie:傳統的 session ID,例如我們之前已經用過了很多年的 Session Cookie。會話相關的數據存儲在服務端。

注意:這篇文章並不是用來說服你絕不要去使用 JWT,而是為了說明 JWT 並不適合用來實現 Session 機制,並且這樣做會帶來一定的風險。在文章的末尾,我也會簡要的介紹一些適合 JWT 使用的場景。

提前注意

很多人總是錯誤的想去比較 cookies 跟 JWT。這種比較是毫無意義的——cookies 是一種存儲機制,而 JWT 是一種加密簽名的令牌機制。

它們之間並不是相互對立的,相反的,它們既可以獨立使用還可以配合使用。真正應該比較的是 session 跟 JWT 以及 cookies 跟 Local Storage。

在這篇文章中,我將主要對 sessions 與 JWT tokens 進行比較,必要時也會穿插一些 cookies 與 Local Storage 之間的對比。

JWT 經常被人宣傳的優點

人們在安利 JWT 的時候經常列出下面這些好處:

  • 容易水平擴展
  • 簡單易用
  • 更加靈活
  • 更加安全
  • 內建的過期功能
  • 不需要向用戶請求“cookie 許可”
  • 預防 CSRF
  • 在移動端表現的更好
  • 就算用戶禁用了 cookies 也能照常工作

接下來我將逐個解釋說明為什么這些所謂的優點都是錯誤的或者具有誤導性的。我的一些解釋可能會顯得有些含糊,但這不能怪我,因為這些所謂的優點本身就是含糊不清的。

容易水平擴展

這是之前列出來的 JWT 優勢列表中唯一在技術層面上說的比較正確的,不過前提是你在使用 無狀態 JWT tokens。然而現實情況是大多數人實際並不要這種可擴展性——我們已經有了很多種簡單的方式來進行水平擴張,除非你的用戶量跟貼吧不相上下,否則你是不需要無狀態會話(斯stateless sessions)的。

下面是一些擴張有狀態會話的例子:

  1. 在單台服務器上運行多個后端進程:可以在同台服務器上運行一個 Redis 來進行會話數據存儲
  2. 在多台服務器上運行后端進程:可以用一個獨立的服務器來運行 Redis 來進行會話存儲
  3. 在多個集群中的多台服務器上運行后端進程:Sticky Sessions。

這些就是現有軟件技術能夠良好支持的場景。通常,你的大多數應用不會到達第三個場景中提到的規模。

或許你認為你應該為你的網站提前做好准備,來應對將來可能會發生的水平擴張。然而,在實際的開發中,替換 session 機制是一項微不足道的工作,而你所需要付出的代價往往只是讓每個用戶強制登出系統一次。考慮到后面我將要列出的一系列缺點,提前去實現 JWT 其實並不划算。

簡單易用

實際上 JWT 並沒有那么簡單易用。你需要同時手動管理客戶端與服務端的會話狀態,然而傳統的 Session cookies 不用我們做額外的工作就能夠正常工作,開箱即用。JWT 從任何方面來看都不夠簡單。

更加靈活

我還沒有見過有人解釋 JWT 是怎么更加靈活的。基本上所有的主流的 seesion 實現都允許你在會話中存儲任意的數據,而這跟 JWT 的工作原理沒有任何不同。據我所知,這只是人雲亦雲的說法。如果你不同意的話,可以聯系我,發一些例子給我瞧瞧。

更加安全

很多人覺得 JWT tokens 更加安全,因為它使用了密碼學算法進行簽名校驗。不過有簽名(signed)的 cookies 也比無簽名(unsigned)的 cookies 要安全得多,所以這項特性並不是 JWT 獨有的,優秀的 seesion 實現都是會使用有簽名 cookies 的。

“使用了密碼學算法進行簽名校驗”並不會神奇地讓一個東西變得更加安全,除非是針對某一特殊目的,且是針對這一特殊目的的一個有效的解決方案。錯誤地使用加密算法有時甚至會讓你的應用變得不夠安全。

另一種對“更加安全”的解釋是“JWT 不通過 cookie 發送”,這簡直是滑天下之大稽——cookie 只是一個普普通通的 HTTP 頭罷了,並不會導致什么安全風險。實際上,cookies 會被特別地對待,來對抗惡意的客戶端代碼,相關的內容稍后我會提到。

如果你真的擔心有人會攔截你的 session cookie,你應該使用 HTTPS——如果不使用 HTTPS 的話,任何類型的 session 實現,包括 JWT, 都是可攔截的。

內建的過期功能

這同樣不是一個 JWT 獨有的功能。過期限制在大多數的框架的 session 實現中都可以做到,甚至還做的更好——你可以做到在不需要會話數據的時候將其完全清除,這是在使用有狀態 JWT 是無法實現的。

不需要向用戶請求“cookie 許可”

這同樣也是滑天下之大稽的說法。從來就沒有什么限制 cookie 使用的法律——有關 cookie 的各種法律實際上涵蓋了所有的非必需的持久性用戶標識符,你可以想到的任何會話管理機制都被包括其中。

簡單來說:

  • 如果你出於實現必須的功能性目的來使用 cookies 或者 tokens 的話(例如,保持用戶的登錄狀態),那么你是不需要征得用戶同意的。
  • 如果你處於其他非必須的功能性目的來使用 cookies 或者 tokens 的話(例如,分析用戶行為),那么你就需要獲得用戶的許可。

預防 CSRF

講真,JWT 並沒有防范 CSRF 攻擊的能力。通常,我們有兩種方式來存儲 JWT:

  • 存儲在 Cookie 中:現在你仍然對 CSRF 攻擊沒有抵抗力,並仍然需要依賴其他的保護措施。
  • 存儲在其他地方,例如,Local Storage:現在你能夠抵抗 CSRF 攻擊了,不過你的網站也需要使用 JS 代碼才能正常工作了,同時也讓你的網站更容易遭到另一種完全不同的甚至更加糟糕的威脅了。更多相關的內容我會在后面提到。

應對 CSRF 攻擊的正確姿勢是使用 CSRF token,而會話管理機制跟其毫無關系。

在移動端表現的更好

這也是毫無根據的,任何移動端的瀏覽器、每一個的主流的移動端開發框架以及嚴謹的 HTTP 庫都是支持 cookies 的,所以也能夠支持 session。所以 session 對移動端的支持不存在任何問題。

就算用戶禁用了 cookies 也能照常工作

未必如此。如果一個用戶禁用了 cookies,那么他通常也會禁用掉其他的持久化手段,包括 Local Storage 以及其他各種能夠直接用來在客戶端存儲會話數據的手段。不管用不用 JWT,這已經變成了另一個問題——而且,嘗試在禁用客戶端存儲的情況下讓身份認證照常工作有點偏離我們的開發目標。

最重要的是,那些禁用了所有 cookies 的用戶通常能夠理解這樣做會讓身份認證功能失效,如果他們在意網站中的這個功能的話,是會單獨的取消對該網站 cookies 的禁用的。所以,這並不是一個網站開發人員需要去解決的一個問題,你更值得去做的是向用戶解釋為什么你的網站需要使用 cookies 才能正常工作。

JWT 的缺點

至此,我已經逐個解釋了為什么 JWT 的那些所謂的優點錯誤的,如果你認為“這沒什么大不了的,就算 JWT 不能幫助我解決更多的問題,也不影響我使用它來進行會話管理”,那你就錯了。使用 JWT 來進行會話管理存在不少缺點,其中一些甚至會造成嚴重安全問題。

占用更多存儲空間

JWT token 通常不會很小,尤其是那些存儲了所有會話數據的無狀態 JWT token。你會很快就達到 Cookie 或者 URL 的容量上限,所以你決定使用 Local Storage 來存儲 JWT token,不過······

更不安全

當你將 JWT 存儲在 Cookie 中的時候,它跟其他的 Session ID 的存儲方式沒有什么區別。但是當你把它存儲在其他地方的時候,你將面對另一種惡意攻擊的威脅。具體的內容在這篇文章中的“Storing sessions”一節中說明了。

We pick up where we left off: back at local storage, an awesome HTML5 addition that adds a key/value store to browsers and cookies. So should we store JWTs in local storage? It might make sense given the size that these tokens can reach. Cookies typically top out somewhere around 4k of storage. For a large-sized token, a cookie might be out of the question and local storage would be the obvious solution. However, local storage doesn’t provide any of the same security mechanisms that cookies do.

Local storage, unlike cookies, doesn’t send the contents of your data store with every single request. The only way to retrieve data out of local storage is by using JavaScript, which means any attacker supplied JavaScript that passes the Content Security Policy can access and exfiltrate it. Not only that, but JavaScript also doesn’t care or track whether or not the data is sent over HTTPS. As far as JavaScript is concerned, it’s just data and the browser will operate on it like it would any other data.

After all the trouble those engineers went through to make sure nobody is going to make off with our cookie jar, here we are trying to ignore all the fancy tricks they’ve given us. That seems a little backwards to me.

我們回到了曾經離開的地方:Local Storage,一個 HTML5 中極好的新功能,為了瀏覽器提供了新的鍵值存儲能力。所以我們應該將 JWT token 存儲在這里面嗎?考慮到 token 可能會達到的大小,這么做是有意義的。Cookies 中往往只能存儲 4k 左右的內容,對於一些大體積的 token 來說,使用 Local Storage 來存儲是一個顯而易見的解決方案。然而 Local Storage 並沒有提供類似 cookie 擁有的安全機制。

與 Cookies 不同,Local Storage 中的內容並不會隨着請求發送。取出 Local Storage 中數據的方法只有使用 JS,這意味着任何繞過了 CSP(Content Security Policy)的惡意腳本都可以獲取其中的內容。除此之外,JS 也不關心這些數據是否是通過 HTTPS 傳輸的,對於 JS 來說,這些僅僅只是普通的數據而已,而且瀏覽器也會像對待其他數據一樣對待存儲在 Local Storage 中的 JWT tokens。

工程師們早就已經通過 Cookies 來解決了這些麻煩,但是在這里我們卻又要放棄這些已有的成果,在我看來像是在開倒車。

簡單來說,除了使用 cookies 來存儲敏感數據,我們別無他選,不管你是否使用 JWT。

你沒法讓單獨的一個 JWT token 失效

使用 Session 的時候,我們可以隨時在服務端讓某個用戶的會話失效,而對於無狀態 JWT 來說,這是無法做到的。JWT 從一開始就被設計為只要不過期就始終保持有效性,這意味着你無法在一些必要的情況下及時的去讓單個無狀態 JWT 失效。例如,當你檢測到用戶賬號被入侵時,或者用戶修改了密碼后,你都沒辦法讓他們的會話狀態失效。

除非你從頭造一個復雜(且有狀態)的模塊來檢測並拒絕需要強行失效的 token,否則你在應對這種情況的時候基本上是無能為力的。

數據會變得陳舊

就像緩存一樣,存儲在無狀態 JWT 中的最終會慢慢的變得陳舊,而且無法及時同步數據庫中的最新數據。

這意味着當一個用戶更新了自己的個人信息之后,之前簽發的 JWT token 中的信息將會變得過時,而另一種更加嚴重的情況是,即便你撤銷了某個用戶的管理員權限,只要在你進行撤銷操作前的 token 沒有過期,你就不能真正的完全禁止他使用管理員權限訪問系統,除非你把整個網站關掉。

JWT 的實現缺乏實戰檢驗

上面我們提到的缺點都是無狀態 JWT 才會有的,那么你可能就會想,用有狀態 JWT 不就好了。然而,有狀態的 JWT 跟普通的 session cookies 並沒有什么太大的區別,而且這些有狀態 JWT 的實現往往沒有經歷過足夠的實戰檢驗。

現有 Session 實現(如,express-session)已經在生產環境中穩定的工作了許多年,在這期間它的安全性也提升了很多。而你自己實現的基於 JWT 的 Session 並不能給你帶來這些安全方面的保證,而且第三方的實現也少有真正在生產環境中使用的。

總結

無狀態 JWT 無法被強制失效或者被及時更新,並且會帶來體積過大或者一些由於存儲不當而造成的安全隱患。有狀態的 JWT 跟 session cookies 並沒有太大差別並且缺乏真實生產環境的檢驗與現有客戶端程序的支持。

除非你正在做一個超大規模的應用程序,那么你就不應該使用 JWT 來管理用戶會話——只要使用 Session 就好了。

JWT 的用武之地

在這片文章的開頭我就說了,JWT 能夠發揮作用的地方不在於實現 session 機制。把 JWT 作為一次性授權 token 使用,才是正確的使用姿勢。

根據 Json Web Token specification

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. [...] enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

JWT 是一種簡潔的,URL 安全的,用來表示在雙方之間傳遞 claims 的方式。它讓 claims 能夠通過消息驗證碼(MAC)進行數字簽名或者加密解密。

這里的 “claim”可以理解為類似“命令”或者需要一次性授權的東西,例如:

你好,服務器 B,服務器 A 跟我說我可以 ,然后這里是(加密后的)憑證。

舉個例子,假設你在運營一個文件存儲服務,用戶需要登陸后才能下載他們的文件,但是文件卻需要通過另一個“無狀態的下載服務”獲取。在這種情況下,你就需要讓你的應用服務器簽發一個“下載 Token”,這樣客戶端就可以通過這個 token 去訪問“下載服務”來下載文件。

以這種方式使用 JWT 的時候,需要注意以下幾點:

  • Token 的有效期要足夠短。它們只要在幾分鍾內有效,能夠讓客戶端發起下載文件的請求就夠了。
  • Token 只能被使用一次。應用服務器應該在每次下載文件的時候簽發一個新的 token,每個 token 只能夠用來請求文件一次,之后就會被拋棄,所以也就不需要去存儲 token 的狀態。
  • 應用服務器仍然需要使用 sessions。只有“下載服務”需要使用 token 來驗證每一次的下載請求,因為它並不會存儲任何狀態。

如你所見,將 JWT 與 Session 結合使用是有意義的——它們之間分工明確,而且有的時候你還需要它們配合工作。最后,不要使用 JWT 來存儲被持久化的或者長期存在的數據。


This post is licensed under the [WTFPL](This post is licensed under the WTFPL. You may distribute, use, modify, translate, and license it in any way. ). You may distribute, use, modify, translate, and license it in any way.

---恢復內容結束---

英文原文地址:Stop using JWT for sessions

最近我發現越來越多的人推薦使用 JWT 來在 Web 應用中管理會話(Session),這是一個非常非常糟糕的主意,在這篇文章中我會詳細地解釋為什么“不要使用 JWT 進行會話管理”。

為了方便敘述的同時避免混淆,我需要先定義一些概念:

  • 無狀態 JWT(Stateless JWT):一個包含了所有會話相關數據的 JWT token。
  • 有狀態 JWT(Stateful JWT):一個僅包含了會話相關數據 ID 的 JWT token,真正的會話相關數據被存儲在服務端。
  • Session token/cookie:傳統的 session ID,例如我們之前已經用過了很多年的 Session Cookie。會話相關的數據存儲在服務端。

注意:這篇文章並不是用來說服你絕不要去使用 JWT,而是為了說明 JWT 並不適合用來實現 Session 機制,並且這樣做會帶來一定的風險。在文章的末尾,我也會簡要的介紹一些適合 JWT 使用的場景。

提前注意

很多人總是錯誤的想去比較 cookies 跟 JWT。這種比較是毫無意義的——cookies 是一種存儲機制,而 JWT 是一種加密簽名的令牌機制。

它們之間並不是相互對立的,相反的,它們既可以獨立使用還可以配合使用。真正應該比較的是 session 跟 JWT 以及 cookies 跟 Local Storage。

在這篇文章中,我將主要對 sessions 與 JWT tokens 進行比較,必要時也會穿插一些 cookies 與 Local Storage 之間的對比。

JWT 經常被人宣傳的優點

人們在安利 JWT 的時候經常列出下面這些好處:

  • 容易水平擴展
  • 簡單易用
  • 更加靈活
  • 更加安全
  • 內建的過期功能
  • 不需要向用戶請求“cookie 許可”
  • 預防 CSRF
  • 在移動端表現的更好
  • 就算用戶禁用了 cookies 也能照常工作

接下來我將逐個解釋說明為什么這些所謂的優點都是錯誤的或者具有誤導性的。我的一些解釋可能會顯得有些含糊,但這不能怪我,因為這些所謂的優點本身就是含糊不清的。

容易水平擴展

這是之前列出來的 JWT 優勢列表中唯一在技術層面上說的比較正確的,不過前提是你在使用 無狀態 JWT tokens。然而現實情況是大多數人實際並不要這種可擴展性——我們已經有了很多種簡單的方式來進行水平擴張,除非你的用戶量跟貼吧不相上下,否則你是不需要無狀態會話(斯stateless sessions)的。

下面是一些擴張有狀態會話的例子:

  1. 在單台服務器上運行多個后端進程:可以在同台服務器上運行一個 Redis 來進行會話數據存儲
  2. 在多台服務器上運行后端進程:可以用一個獨立的服務器來運行 Redis 來進行會話存儲
  3. 在多個集群中的多台服務器上運行后端進程:Sticky Sessions。

這些就是現有軟件技術能夠良好支持的場景。通常,你的大多數應用不會到達第三個場景中提到的規模。

或許你認為你應該為你的網站提前做好准備,來應對將來可能會發生的水平擴張。然而,在實際的開發中,替換 session 機制是一項微不足道的工作,而你所需要付出的代價往往只是讓每個用戶強制登出系統一次。考慮到后面我將要列出的一系列缺點,提前去實現 JWT 其實並不划算。

簡單易用

實際上 JWT 並沒有那么簡單易用。你需要同時手動管理客戶端與服務端的會話狀態,然而傳統的 Session cookies 不用我們做額外的工作就能夠正常工作,開箱即用。JWT 從任何方面來看都不夠簡單。

更加靈活

我還沒有見過有人解釋 JWT 是怎么更加靈活的。基本上所有的主流的 seesion 實現都允許你在會話中存儲任意的數據,而這跟 JWT 的工作原理沒有任何不同。據我所知,這只是人雲亦雲的說法。如果你不同意的話,可以聯系我,發一些例子給我瞧瞧。

更加安全

很多人覺得 JWT tokens 更加安全,因為它使用了密碼學算法進行簽名校驗。不過有簽名(signed)的 cookies 也比無簽名(unsigned)的 cookies 要安全得多,所以這項特性並不是 JWT 獨有的,優秀的 seesion 實現都是會使用有簽名 cookies 的。

“使用了密碼學算法進行簽名校驗”並不會神奇地讓一個東西變得更加安全,除非是針對某一特殊目的,且是針對這一特殊目的的一個有效的解決方案。錯誤地使用加密算法有時甚至會讓你的應用變得不夠安全。

另一種對“更加安全”的解釋是“JWT 不通過 cookie 發送”,這簡直是滑天下之大稽——cookie 只是一個普普通通的 HTTP 頭罷了,並不會導致什么安全風險。實際上,cookies 會被特別地對待,來對抗惡意的客戶端代碼,相關的內容稍后我會提到。

如果你真的擔心有人會攔截你的 session cookie,你應該使用 HTTPS——如果不使用 HTTPS 的話,任何類型的 session 實現,包括 JWT, 都是可攔截的。

內建的過期功能

這同樣不是一個 JWT 獨有的功能。過期限制在大多數的框架的 session 實現中都可以做到,甚至還做的更好——你可以做到在不需要會話數據的時候將其完全清除,這是在使用有狀態 JWT 是無法實現的。

不需要向用戶請求“cookie 許可”

這同樣也是滑天下之大稽的說法。從來就沒有什么限制 cookie 使用的法律——有關 cookie 的各種法律實際上涵蓋了所有的非必需的持久性用戶標識符,你可以想到的任何會話管理機制都被包括其中。

簡單來說:

  • 如果你出於實現必須的功能性目的來使用 cookies 或者 tokens 的話(例如,保持用戶的登錄狀態),那么你是不需要征得用戶同意的。
  • 如果你處於其他非必須的功能性目的來使用 cookies 或者 tokens 的話(例如,分析用戶行為),那么你就需要獲得用戶的許可。

預防 CSRF

講真,JWT 並沒有防范 CSRF 攻擊的能力。通常,我們有兩種方式來存儲 JWT:

  • 存儲在 Cookie 中:現在你仍然對 CSRF 攻擊沒有抵抗力,並仍然需要依賴其他的保護措施。
  • 存儲在其他地方,例如,Local Storage:現在你能夠抵抗 CSRF 攻擊了,不過你的網站也需要使用 JS 代碼才能正常工作了,同時也讓你的網站更容易遭到另一種完全不同的甚至更加糟糕的威脅了。更多相關的內容我會在后面提到。

應對 CSRF 攻擊的正確姿勢是使用 CSRF token,而會話管理機制跟其毫無關系。

在移動端表現的更好

這也是毫無根據的,任何移動端的瀏覽器、每一個的主流的移動端開發框架以及嚴謹的 HTTP 庫都是支持 cookies 的,所以也能夠支持 session。所以 session 對移動端的支持不存在任何問題。

就算用戶禁用了 cookies 也能照常工作

未必如此。如果一個用戶禁用了 cookies,那么他通常也會禁用掉其他的持久化手段,包括 Local Storage 以及其他各種能夠直接用來在客戶端存儲會話數據的手段。不管用不用 JWT,這已經變成了另一個問題——而且,嘗試在禁用客戶端存儲的情況下讓身份認證照常工作有點偏離我們的開發目標。

最重要的是,那些禁用了所有 cookies 的用戶通常能夠理解這樣做會讓身份認證功能失效,如果他們在意網站中的這個功能的話,是會單獨的取消對該網站 cookies 的禁用的。所以,這並不是一個網站開發人員需要去解決的一個問題,你更值得去做的是向用戶解釋為什么你的網站需要使用 cookies 才能正常工作。

JWT 的缺點

至此,我已經逐個解釋了為什么 JWT 的那些所謂的優點錯誤的,如果你認為“這沒什么大不了的,就算 JWT 不能幫助我解決更多的問題,也不影響我使用它來進行會話管理”,那你就錯了。使用 JWT 來進行會話管理存在不少缺點,其中一些甚至會造成嚴重安全問題。

占用更多存儲空間

JWT token 通常不會很小,尤其是那些存儲了所有會話數據的無狀態 JWT token。你會很快就達到 Cookie 或者 URL 的容量上限,所以你決定使用 Local Storage 來存儲 JWT token,不過······

更不安全

當你將 JWT 存儲在 Cookie 中的時候,它跟其他的 Session ID 的存儲方式沒有什么區別。但是當你把它存儲在其他地方的時候,你將面對另一種惡意攻擊的威脅。具體的內容在這篇文章中的“Storing sessions”一節中說明了。

We pick up where we left off: back at local storage, an awesome HTML5 addition that adds a key/value store to browsers and cookies. So should we store JWTs in local storage? It might make sense given the size that these tokens can reach. Cookies typically top out somewhere around 4k of storage. For a large-sized token, a cookie might be out of the question and local storage would be the obvious solution. However, local storage doesn’t provide any of the same security mechanisms that cookies do.

Local storage, unlike cookies, doesn’t send the contents of your data store with every single request. The only way to retrieve data out of local storage is by using JavaScript, which means any attacker supplied JavaScript that passes the Content Security Policy can access and exfiltrate it. Not only that, but JavaScript also doesn’t care or track whether or not the data is sent over HTTPS. As far as JavaScript is concerned, it’s just data and the browser will operate on it like it would any other data.

After all the trouble those engineers went through to make sure nobody is going to make off with our cookie jar, here we are trying to ignore all the fancy tricks they’ve given us. That seems a little backwards to me.

我們回到了曾經離開的地方:Local Storage,一個 HTML5 中極好的新功能,為了瀏覽器提供了新的鍵值存儲能力。所以我們應該將 JWT token 存儲在這里面嗎?考慮到 token 可能會達到的大小,這么做是有意義的。Cookies 中往往只能存儲 4k 左右的內容,對於一些大體積的 token 來說,使用 Local Storage 來存儲是一個顯而易見的解決方案。然而 Local Storage 並沒有提供類似 cookie 擁有的安全機制。

與 Cookies 不同,Local Storage 中的內容並不會隨着請求發送。取出 Local Storage 中數據的方法只有使用 JS,這意味着任何繞過了 CSP(Content Security Policy)的惡意腳本都可以獲取其中的內容。除此之外,JS 也不關心這些數據是否是通過 HTTPS 傳輸的,對於 JS 來說,這些僅僅只是普通的數據而已,而且瀏覽器也會像對待其他數據一樣對待存儲在 Local Storage 中的 JWT tokens。

工程師們早就已經通過 Cookies 來解決了這些麻煩,但是在這里我們卻又要放棄這些已有的成果,在我看來像是在開倒車。

簡單來說,除了使用 cookies 來存儲敏感數據,我們別無他選,不管你是否使用 JWT。

你沒法讓單獨的一個 JWT token 失效

使用 Session 的時候,我們可以隨時在服務端讓某個用戶的會話失效,而對於無狀態 JWT 來說,這是無法做到的。JWT 從一開始就被設計為只要不過期就始終保持有效性,這意味着你無法在一些必要的情況下及時的去讓單個無狀態 JWT 失效。例如,當你檢測到用戶賬號被入侵時,或者用戶修改了密碼后,你都沒辦法讓他們的會話狀態失效。

除非你從頭造一個復雜(且有狀態)的模塊來檢測並拒絕需要強行失效的 token,否則你在應對這種情況的時候基本上是無能為力的。

數據會變得陳舊

就像緩存一樣,存儲在無狀態 JWT 中的最終會慢慢的變得陳舊,而且無法及時同步數據庫中的最新數據。

這意味着當一個用戶更新了自己的個人信息之后,之前簽發的 JWT token 中的信息將會變得過時,而另一種更加嚴重的情況是,即便你撤銷了某個用戶的管理員權限,只要在你進行撤銷操作前的 token 沒有過期,你就不能真正的完全禁止他使用管理員權限訪問系統,除非你把整個網站關掉。

JWT 的實現缺乏實戰檢驗

上面我們提到的缺點都是無狀態 JWT 才會有的,那么你可能就會想,用有狀態 JWT 不就好了。然而,有狀態的 JWT 跟普通的 session cookies 並沒有什么太大的區別,而且這些有狀態 JWT 的實現往往沒有經歷過足夠的實戰檢驗。

現有 Session 實現(如,express-session)已經在生產環境中穩定的工作了許多年,在這期間它的安全性也提升了很多。而你自己實現的基於 JWT 的 Session 並不能給你帶來這些安全方面的保證,而且第三方的實現也少有真正在生產環境中使用的。

總結

無狀態 JWT 無法被強制失效或者被及時更新,並且會帶來體積過大或者一些由於存儲不當而造成的安全隱患。有狀態的 JWT 跟 session cookies 並沒有太大差別並且缺乏真實生產環境的檢驗與現有客戶端程序的支持。

除非你正在做一個超大規模的應用程序,那么你就不應該使用 JWT 來管理用戶會話——只要使用 Session 就好了。

JWT 的用武之地

在這片文章的開頭我就說了,JWT 能夠發揮作用的地方不在於實現 session 機制。把 JWT 作為一次性授權 token 使用,才是正確的使用姿勢。

根據 Json Web Token specification

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. [...] enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

JWT 是一種簡潔的,URL 安全的,用來表示在雙方之間傳遞 claims 的方式。它讓 claims 能夠通過消息驗證碼(MAC)進行數字簽名或者加密解密。

這里的 “claim”可以理解為類似“命令”或者需要一次性授權的東西,例如:

你好,服務器 B,服務器 A 跟我說我可以 ,然后這里是(加密后的)憑證。

舉個例子,假設你在運營一個文件存儲服務,用戶需要登陸后才能下載他們的文件,但是文件卻需要通過另一個“無狀態的下載服務”獲取。在這種情況下,你就需要讓你的應用服務器簽發一個“下載 Token”,這樣客戶端就可以通過這個 token 去訪問“下載服務”來下載文件。

以這種方式使用 JWT 的時候,需要注意以下幾點:

  • Token 的有效期要足夠短。它們只要在幾分鍾內有效,能夠讓客戶端發起下載文件的請求就夠了。
  • Token 只能被使用一次。應用服務器應該在每次下載文件的時候簽發一個新的 token,每個 token 只能夠用來請求文件一次,之后就會被拋棄,所以也就不需要去存儲 token 的狀態。
  • 應用服務器仍然需要使用 sessions。只有“下載服務”需要使用 token 來驗證每一次的下載請求,因為它並不會存儲任何狀態。

如你所見,將 JWT 與 Session 結合使用是有意義的——它們之間分工明確,而且有的時候你還需要它們配合工作。最后,不要使用 JWT 來存儲被持久化的或者長期存在的數據。


This post is licensed under the [WTFPL](This post is licensed under the WTFPL. You may distribute, use, modify, translate, and license it in any way. ). You may distribute, use, modify, translate, and license it in any way.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM