理解JWT鑑權的應用場景及使用建議

Java知音發表於2019-08-19

作者:maotou叔叔

cnblogs.com/mantoudev/p/8994341.html

1. JWT 介紹

JSON Web Token(JWT)是一個開放式標準(RFC 7519),它定義了一種緊湊(Compact)且自包含(Self-contained)的方式,用於在各方之間以JSON物件安全傳輸資訊。這些資訊可以通過數字簽名進行驗證和信任。可以使用祕密(使用HMAC演算法)或使用RSA的公鑰/私鑰對對JWT進行簽名。

理解JWT鑑權的應用場景及使用建議

雖然JWT可以加密以提供各方之間的保密性,但我們將重點關注已簽名的令牌。簽名的令牌可以驗證其中包含的索賠的完整性,而加密令牌隱藏來自其他方的索賠。當令牌使用公鑰/私鑰對進行簽名時,簽名還證明只有持有私鑰的方是簽名方。

我們來進一步解釋一些概念:

Compact(緊湊):

由於它們尺寸較小,JWT可以通過URL,POST引數或HTTP標頭內傳送。另外,尺寸越小意味著傳輸速度越快。

Self-contained(自包含):

有效載荷(Playload)包含有關使用者的所有必需資訊,避免了多次查詢資料庫。

2. JWT適用場景

Authentication(鑑權):

這是使用JWT最常見的情況。一旦使用者登入,每個後續請求都將包含JWT,允許使用者訪問該令牌允許的路由,服務和資源。單點登入是當今廣泛使用JWT的一項功能,因為它的開銷很小,並且能夠輕鬆地跨不同域使用。

Information Exchange(資訊交換):

JSON Web Tokens是在各方之間安全傳輸資訊的好方式。因為JWT可以簽名:例如使用公鑰/私鑰對,所以可以確定發件人是他們自稱的人。此外,由於使用標頭和有效載荷計算簽名,因此您還可以驗證內容是否未被篡改。

3. JWT結構

在緊湊的形式中,JWT包含三個由點(.)分隔的部分,它們分別是:

  • Header

  • Payload

  • Signature

JWT結構通常如下所示:

xxxxx.yyyyy.zzzzz複製程式碼

下面我們分別來介紹這三個部分:

Header

Header通常由兩部分組成:令牌的型別,即JWT。和常用的雜湊演算法,如HMAC SHA256或RSA。

例如:

{  "alg": "HS256",  "typ": "JWT"}複製程式碼

Header部分的JSON被Base64Url編碼,形成JWT的第一部分。

Payload

這裡放宣告內容,可以說就是存放溝通訊息的地方,在定義上有3種宣告(Claims):

Registered claims(註冊宣告):

這些是一組預先定義的宣告,它們不是強制性的,但推薦使用,以提供一組有用的,可互操作的宣告。其中一些是:iss(發行者),exp(到期時間),sub(主題),aud(受眾)等。

Public claims(公開宣告):

這些可以由使用JWT的人員隨意定義。但為避免衝突,應在IANA JSON Web令牌登錄檔中定義它們,或將其定義為包含防衝突名稱空間的URI。

Private claims(私有宣告):

這些是為了同意使用它們但是既沒有登記,也沒有公開宣告的各方之間共享資訊,而建立的定製宣告。

Playload示例如下:

{  "sub": "1234567890",  "name": "John Doe",  "admin": true}複製程式碼

Playload部分的JSON被Base64Url編碼,形成JWT的第二部分。

Notice:

請注意,對於已簽名的令牌,此資訊儘管受到篡改保護,但任何人都可以閱讀。除非加密,否則不要將祕密資訊放在JWT的有效內容或標題元素中。這也是很多文章爭論jwt安全性原因,不要用 JWT 取代 Server-side 的 Session狀態機制。詳情請閱讀這篇文章:
http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/

Signature

第三部分signature用來驗證傳送請求者身份,由前兩部分加密形成。

要建立簽名部分,您必須採用編碼標頭,編碼有效載荷,祕鑰,標頭中指定的演算法並簽名。

例如,如果你想使用HMAC SHA256演算法,簽名將按照以下方式建立:

HMACSHA256(  base64UrlEncode(header) + "." +  base64UrlEncode(payload),  secret)複製程式碼

3. JWT實踐

JWT輸出的是三個由點分隔的Base64-URL字串,可以在HTML和HTTP環境中輕鬆傳遞,而與基於XML的標準(如SAML)相比,它更加緊湊。

以下JWT示例,它具有先前的標頭和有效負載編碼,並且使用祕鑰進行簽名。

理解JWT鑑權的應用場景及使用建議

我們可以使用jwt.io偵錯程式來解碼,驗證和生成JWT:

理解JWT鑑權的應用場景及使用建議

4.JWT工作原理

在身份驗證中,當使用者使用他們的憑證成功登入時,JSON Web Token將被返回並且必須儲存在本地(通常在本地儲存中,但也可以使用Cookie),而不是在傳統方法中建立會話 伺服器並返回一個cookie。

關於儲存令牌(Token)的方式,必須考慮安全因素。

參考:

https://auth0.com/docs/security/store-tokens

無論何時使用者想要訪問受保護的路由或資源,使用者代理都應使用承載方案傳送JWT,通常在請求頭中的Authorization欄位,使用Bearer schema:

Authorization: Bearer <token>複製程式碼

這是一種無狀態身份驗證機制,因為使用者狀態永遠不會儲存在伺服器記憶體中。伺服器受保護的路由將在授權頭中檢查有效的JWT,如果存在,則允許使用者訪問受保護的資源。由於JWT是獨立的,所有必要的資訊都在那裡,減少了多次查詢資料庫的需求。

這使得我們可以完全依賴無狀態的資料API,甚至向下遊服務提出請求。無論哪些域正在為API提供服務並不重要,因此不會出現跨域資源共享(CORS)的問題,因為它不使用Cookie。

理解JWT鑑權的應用場景及使用建議

Notice:

請注意,使用已簽名的令牌,令牌中包含的所有資訊都會暴露給使用者或其他方,即使他們無法更改它。在JWT中,不應該在Playload裡面加入任何敏感的資料,比如像密碼這樣的內容。如果將使用者的密碼放在了JWT中,那麼懷有惡意的第三方通過Base64解碼就能很快地知道你的密碼了。

5. 常見問題

① JWT 安全嗎?

Base64編碼方式是可逆的,也就是透過編碼後發放的Token內容是可以被解析的。一般而言,我們都不建議在有效載荷內放敏感訊息,比如使用者的密碼。

② JWT Payload 內容可以被偽造嗎?

JWT其中的一個組成內容為Signature,可以防止通過Base64可逆方法回推有效載荷內容並將其修改。因為Signature是經由Header跟Payload一起Base64組成的。

③ 如果我的 Cookie 被竊取了,那不就表示第三方可以做 CSRF 攻擊?

是的,Cookie丟失,就表示身份就可以被偽造。故官方建議的使用方式是存放在LocalStorage中,並放在請求頭中傳送。

④ 空間及長度問題?

JWT Token通常長度不會太小,特別是Stateless JWT Token,把所有的資料都編在Token裡,很快的就會超過Cookie的大小(4K)或者是URL長度限制。

⑤ Token失效問題?

無狀態JWT令牌(Stateless JWT Token)發放出去之後,不能通過伺服器端讓令牌失效,必須等到過期時間過才會失去效用。

假設在這之間Token被攔截,或者有許可權管理身份的差異造成授權Scope修改,都不能阻止發出去的Token失效並要求使用者重新請求新的Token。

6. JWT使用建議

  • 不要存放敏感資訊在Token裡。

  • Payload中的exp時效不要設定太長。

  • 開啟Only Http預防XSS攻擊。

  • 如果擔心重播攻擊(replay attacks )可以增加jti(JWT ID),exp(有效時間) Claim。

  • 在你的應用程式應用層中增加黑名單機制,必要的時候可以進行Block做阻擋(這是針對掉令牌被第三方使用竊取的手動防禦)。

參考

http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
https://stormpath.com/blog/jwt-the-right-way
https://en.wikipedia.org/wiki/JSON_Web_Token

推薦閱讀(點選即可跳轉閱讀)


1. SpringBoot內容聚合

2. 面試題內容聚合

3. 設計模式內容聚合

4. Mybatis內容聚合

5. 多執行緒內容聚合

Javaç¥é³å®æ¹å…¬ä¼å·


相關文章