現在很多人使用 JWT 用作 session 管理,這是個糟糕的做法,下面闡述原因,有不同意見的同學歡迎討論。
首先說明一下,JWT 有兩種:
-
無狀態的 JWT,token 中包含 session 數據。
-
有狀態的 JWT,token 中僅有 session ID,session 數據還是存儲在服務端。
本文討論的是 “無狀態的 JWT”,就是把用戶的 session 數據放到 token 中。
JWT 不適合做為 session 機制,這么做是有危險的。
很多人喜歡比較 “cookies vs. JWT”,這種比較是無意義的,就像比較蘋果和桔子,cookies 是一個存儲機制,而 JWT 是加密簽名的 token,他們不是對立的,可以一起使用,或者獨立使用。
正確的比較是“sessions vs. JWT” 和 “cookies vs. Local Storage”。
JWT 宣稱的優點
人們通常會說 JWT 有如下的好處:
- 易於水平擴展
- 簡單易用
- 加密,更安全
- 內置過期功能
- 可以防護 CSRF 攻擊
- 在用戶阻止了cookies后還可以工作
對於這些所謂的好處我們會一一剖析。
(1)易於水平擴展
把 session 數據放入 JWT,服務端不需要保存 session 信息,那么服務端自然是無狀態的,可以隨意擴展。
看上去的確帶來了擴展上的便利,但實際上沒啥優勢,服務器端保存 session 沒有任何難度:
- 多服務器場景:可以用專門的 redis 服務器保存 session。
- 多集群場景:多集群間不需要共享 session,同一個用戶始終分配到同一個集群即可。
這都是很成熟的解決方案,沒有必要在客戶端 token 中保存 session。
(2)簡單易用
看似一個小小的 token 比較簡單,實則不然,你需要自己去處理 session 的管理,怎么可能比開箱即用的 cookies 更簡單。
(3)更安全
因為 JWT 是加密的,很多人認為其更加安全,cookies 沒加密,不安全。
其實 cookies 只是存儲機制,你完全可以使用加密簽名的方式。
還有人認為 cookies 沒加密會被攔截讀取。
cookies 只是 HTTP 頭信息,不負責安全,這個問題應該使用 TLS 來解決,否則,即使 JWT 也沒法保證信息的安全。
(4)內置過期功能
這個功能更是沒用,是否過期應該由服務端控制,不應交給客戶端控制,否則,如果 token 被盜取,服務端將沒有任何辦法。
(5)可以防護 CSRF 攻擊
簡單說一下什么是 CSRF 攻擊。
打個比方,你登錄了網銀,這時已經具備給其他賬戶轉賬的條件了,比如轉賬的接口地址是:
http://xbank.com/transfer?to=123&money=1000
在你沒有退出網銀之前,你訪問了一個惡意網站,其中有一段代碼:
<img src=http://xbank.com/transfer?to=456789&money=1000>
這樣你就丟錢了。
這只是個朴實的例子,實際情況比這復雜得多,但歸根結底,CSRF 攻擊就是源於:隱式身份驗證機制,就是 cookies 是自動發送給服務端的,無法保證該請求是用戶批准的。
所以,很多人就認為使用 JWT 就沒這個問題了,因為不用 cookies 了。
那么請問,你是把 JWT 保存在哪里的?有2個保存方式:
- cookie:這樣同樣會面臨 CSRF 攻擊。
- 其他地方,例如 Local Storage:這樣的確避免了 CSRF,但暴露了更嚴重的安全問題,Local Storage 這類的本地存儲是可以被 JS 直接讀取的。
其實,防護 CSRF 攻擊的正確方式只有:CSRF token。
(6)在用戶阻止了cookies后還可以工作
不幸的是,在用戶阻止了 cookies 的場景中,通常不僅僅是阻止 cookies,而是阻止所有的本地存儲,包括 Local Storage。
這樣的話,JWT 也同樣無法工作。
JWT 的缺點
(1)體積大
如果把 session 信息編碼后放入 token,那么其體積會很大,很有可能超出 cookie 的大小限制,那就只能把 JWT 保存在 Local Storage 了,也就產生了安全問題。
而且,體積大,網絡壓力就大了。
(2)不安全
這個問題上面分析過了,如果放在 cookie 里,那和傳統的 session 方案就差不多了,如果放在別的地方,就有其他安全問題了。
(3)無法使某個 JWT 無效
不像 session,在服務端可以使其失效,而 JWT 直到其過期才能無效。
比如服務端檢測到了一個安全威脅,也無法使相關的 JWT 失效。
也就是說,當你發現風險時,無法殺死某個 session,如果你想解決這個問題,就需要服務端可以對 session 進行管理,那么就變回有狀態的模式了。
(4)session 數據舊了
session 數據是保存在 JWT 中的,其中會有用戶的相關信息,例如角色。
在 JWT 過期之前,用戶的角色發生了變化,那么這時 JWT 中的信息就是舊的了,因為無法更新。
小結
JWT 真的不適合當做 session 使用,JWT 更適合一次性的命令認證,設置一個很短的有效期。
本文的目的不是說 JWT 不好,而是想說把 JWT 用作 session 是用錯了地方。
你是怎么使用 JWT 的?歡迎交流討論。
翻譯整理自:
http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
推薦閱讀: