1. JWT 介紹
JSON Web Token(JWT)是一個開放式标準(RFC 7519),它定義了一種緊湊(Compact)且自包含(Self-contained)的方式,用于在各方之間以JSON對象安全傳輸資訊。 這些資訊可以通過數字簽名進行驗證和信任。 可以使用秘密(使用HMAC算法)或使用RSA的公鑰/私鑰對對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通常由兩部分組成:令牌的類型,即JWT。和常用的雜湊演算法,如HMAC SHA256或RSA。
例如:
{
"alg": "HS256",
"typ": "JWT"
}
Header部分的JSON被Base64Url編碼,形成JWT的第一部分。
這裡放聲明内容,可以說就是存放溝通訊息的地方,在定義上有3種聲明(Claims):
-
Registered claims(注冊聲明):
這些是一組預先定義的聲明,它們不是強制性的,但推薦使用,以提供一組有用的,可互操作的聲明。 其中一些是:
(發行者),iss
(到期時間),exp
(主題),sub
(閱聽人)等。 #Registered Claim Names#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狀态機制。詳情請閱讀這篇文章: Stop Using Jwt For Sessions .
第三部分signature用來驗證發送請求者身份,由前兩部分加密形成。
要建立簽名部分,您必須采用編碼标頭,編碼有效載荷,秘鑰,标頭中指定的算法并簽名。
例如,如果你想使用HMAC SHA256算法,簽名将按照以下方式建立:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
3. JWT實踐
JWT輸出的是三個由點分隔的Base64-URL字元串,可以在HTML和HTTP環境中輕松傳遞,而與基于XML的标準(如SAML)相比,它更加緊湊。
以下JWT示例,它具有先前的标頭和有效負載編碼,并且使用秘鑰進行簽名。

我們可以使用jwt.io調試器來解碼,驗證和生成JWT:
4.JWT工作原理
在身份驗證中,當使用者使用他們的憑證成功登入時,JSON Web Token将被傳回并且必須儲存在本地(通常在本地存儲中,但也可以使用Cookie),而不是在傳統方法中建立會話 伺服器并傳回一個cookie。
關于存儲令牌(Token)的方式,必須考慮安全因素。
參考:
#Where to Store Tokens#
無論何時使用者想要通路受保護的路由或資源,使用者代理都應使用承載方案發送JWT,通常在請求頭中的
Authorization
字段,使用
Bearer
schema:
Authorization: Bearer <token>
這是一種無狀态身份驗證機制,因為使用者狀态永遠不會儲存在伺服器記憶體中。 伺服器受保護的路由将在授權頭中檢查有效的JWT,如果存在,則允許使用者通路受保護的資源。 由于JWT是獨立的,所有必要的資訊都在那裡,減少了多次查詢資料庫的需求。
這使得我們可以完全依賴無狀态的資料API,甚至向下遊服務提出請求。 無論哪些域正在為API提供服務并不重要,是以不會出現跨域資源共享(CORS)的問題,因為它不使用Cookie。
請注意,使用已簽名的令牌,令牌中包含的所有資訊都會暴露給使用者或其他方,即使他們無法更改它。 在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
- 開啟
預防XSS攻擊。Only Http
- 如果擔心重播攻擊(replay attacks )可以增加
(JWT ID),jti
(有效時間) Claim。exp
- 在你的應用程式應用層中增加黑名單機制,必要的時候可以進行Block做阻擋(這是針對掉令牌被第三方使用竊取的手動防禦)。
[1] Stop using JWT for sessions:
http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/[3] Use JWT The Right Way!:
https://stormpath.com/blog/jwt-the-right-way[2] JSON Web Token 維基百科:
https://en.wikipedia.org/wiki/JSON_Web_Token我的部落格即将入駐“雲栖社群”,誠邀技術同仁一同入駐。