新聞動態(tài)
你可能沒那么了解 JWT
常見問題 發(fā)布者:ou3377 2021-12-08 09:04 訪問量:261
最近在開發(fā)一個統(tǒng)一認證服務(wù),涉及到 OIDC
協(xié)議,其中授權(quán)碼模式所頒發(fā)的 id_token 使用的是 JWT ( JSON Web Token ) ,因為這次使用的庫的默認簽名算法和以往不同,所以特地去翻閱了 JWT 的 RFC 文檔( RFC 7519[1] ),一番閱讀后發(fā)現(xiàn)原來對 JWT 的認知只停留在表面,還有更多深層的內(nèi)容是不知道的。
每次提到無狀態(tài)的 JWT 時相信都會看到另一種基于 Session 的用戶認證方案介紹,這里也不例外,Session 的認證流程通常會像這樣:
這種方案有一些缺點:
JWT 正好可以解決這些問題:
JWT 的魔法很簡單,將需要使用到的用戶數(shù)據(jù)等信息放入 JWT 里面,每次請求都會攜帶上,只要保證密鑰不泄露,JWT 就無法偽造。
一個簡單的 JWT 示例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDIxLTEwLTI0IDAwOjAwOjAwIiwibmFtZSI6InRvZ2V0dG95b3UifQ.XdF46NflSUjnt-adAc6rNZEXI1OD6nxtwGuhz9qkxUA
jwt.io[2] 這個網(wǎng)站相信沒有人不知道了,把上面的 JWT 復(fù)制粘貼到網(wǎng)站中
可以看出 JWT 以不同顏色區(qū)分,兩個小數(shù)點隔開,分為了三部分:
① Header(頭部):JSON 對象,描述 JWT 的元數(shù)據(jù)。其中 alg 屬性表示簽名的算法(algorithm),默認是 HMAC SHA256(寫成 HS256);typ 屬性表示這個令牌(token)的類型(type),統(tǒng)一寫為 JWT
② Payload(載荷):JSON 對象,存放實際需要傳遞的數(shù)據(jù),支持自定義字段
③ Signature(簽名):這部分就是 JWT 防篡改的精髓,其值是對前兩部分 base64UrlEncode 后使用指定算法簽名生成,以默認 HS256 為例,指定一個密鑰(secret),就會按照如下公式生成:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret,
)
到這里,大多數(shù)人對 JWT 的認知應(yīng)該是停留在此了,日常使用也已經(jīng)足夠,但你想更深入了解 JWT 的話,那你就得知道 JOSE 。
什么是 JOSE ,它和 JWT 之間又有什么關(guān)系呢。
JOSE 全稱 JSON Object Signing and Encryption ( RFC 7165[3] , RFC 7520[4] ),它定義了一系列的標準,用來規(guī)范網(wǎng)絡(luò)傳輸過程中使用 JSON 的方式,我們上面一直說的 JWT 其實是 JOSE 體系之一。
其中 JWT 又可分為 JWS 和 JWE 兩種不同的實現(xiàn),我們大部分日常所使用的,所說的 JWT 其實應(yīng)該屬于 JWS 。 為什么這么說,請看下文。
JWA 的全稱是 JSON Web Algorithms ( RFC 7518[5] ) ,字如其名, JOSE 體系中涉及到的所有算法就是它來定義的,比如通用算法有 Base64-URL 和 SHA,簽名算法有 HMAC,RSA 和 Elliptic Curve(EC 橢圓曲線),本文不會深入到算法原理(我也不懂),只是想讓你知道 JWA 是做什么的。我們上面的 JWT 例子中第一部分 Header 有個 alg 屬性,其值是 HS256 ,也就是 HMAC + SHA256 算法。
說了那么多,好像都沒有正式介紹過 JWS 。JWS 的全稱是 JSON Web Signature ( RFC 7515[6] ) ,它的核心就是簽名,保證數(shù)據(jù)未被篡改,而檢查簽名的過程就叫做驗證。更通俗的理解,就是對應(yīng)前面提到的 JWT 的第三部分 Signature ,所以我才會說我們?nèi)粘K褂玫?JWT 都是 JWS 。
通常在客戶端-服務(wù)端模式中,JWS 使用 JWA 提供的 HS256 算法加上一個密鑰即可,這種方式嚴格依賴密鑰,但在分布式場景,可能多個服務(wù)都需要驗證 JWT ,若要在每個服務(wù)里面都保存密鑰,那么安全性將會大打折扣,要知道,密鑰一旦泄露,任何人都可以隨意偽造 JWT 。
解決辦法就是使用非對稱加密算法 RSA ,RSA 有兩把鑰匙,一把公鑰,一把私鑰,可以使用私鑰簽發(fā)(簽名分發(fā)) JWT ,使用公鑰驗證 JWT ,公鑰是所有人都可以獲取到的。這樣一來,就只有認證服務(wù)保存著私鑰,進行簽發(fā),其他服務(wù)只能驗證。
如下是一個使用 RS256 ( RSA + SHA256 ) 算法生成的 JWT :
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjF6aXlIVG15M184MDRDOU1jUENHVERmYWJCNThBNENlZG9Wa3VweXdVeU0ifQ.eyJqdGkiOiIzWUJ5eWZ2TDB4b01QNXdwTXVsZ0wiLCJzdWIiOiI2MDE5NDI5NjgwMWRjN2JjMmExYjI3MzUiLCJpYXQiOjE2MTI0NDQ4NzEsImV4cCI6MTYxMzY1NDQ3MSwic2NvcGUiOiJvcGVuaWQgZW1haWwgbWVzc2FnZSIsImlzcyI6Imh0dHBzOi8vc3RlYW0tdGFsay5hdXRoaW5nLmNuL29pZGMiLCJhdWQiOiI2MDE5M2M2MTBmOTExN2U3Y2IwNDkxNTkifQ.cYyZ6buwAjp7DzrYQEhvz5rvUBhkv_s8xzuv2JHgzYx0jbqqsWrA_-gufLTFGmNkZkZwPnF6ktjvPHFT-1iJfWGRruOOMV9QKPhk0S5L2eedtbKJU6XIEkl3F9KbOFwYM53v3E7_VC8RBj5IKqEY0qd4mW36C9VbS695wZlvMYnmXhIopYsd5c83i39fLBF8vEBZE1Rq6tqTQTbHAasR2eUz1LnOqxNp2NNkV2dzlcNIksSDbEGjTNkWceeTWBRtFMi_o9EWaHExdm5574jQ-ei5zE4L7x-zfp9iAe8neuAgTsqXOa6RJswhyn53cW4DwWg_g26lHJZXQvv_RHZRlQ
把它復(fù)制到 jwt.io 上面看看
注意我綠色框選中的地方,里面是一段 JSON ,我們把它刪掉,看看輸入框的提示信息
這里提示了,里面是填寫公鑰格式(通常為 PEM)或者 JWK (我們說過 RSA 算法是使用私鑰簽發(fā) JWT,公鑰進行驗證),剛剛我們刪掉的是一段 JSON,所以必然不是公鑰格式,那是 JWK 嗎?
當然是,JWK 的全稱是 JSON Web Key ( RFC 7517[7] ) ,它就是一個 JSON ,JWK 就是用 JSON 來表示密鑰(JSON 字段因密鑰類型而異)。例如剛才刪除的 JWK :
{
"e": "AQAB",
"kty": "RSA",
"n": "wVKQLBUqOBiay2dkn9TlbfuaF40_edIKUmdLq6OlvzEMrP4IDzdOk50TMO0nfjJ6v5830_5x0vRg5bzZQeKpHniR0sw7qyoSI6n2eSkSnFt7P-N8gv2KWnwzVs_h9FDdeLOeVOU8k_qzkph3_tmBV7ZZG-4_DEvgvat6ifEC-WzzYqofsIrTiTT7ZFxTqid1q6zrrsmyU2DQH3WdgFiOJVVlN2D0BuZu5X7pGZup_RcWzt_9T6tQsGeU1juSuuUk_9_FVDXNNCTObfKCTKXqjW95ZgAI_xVrMeQC5nXlMh6VEaXfO83oy1j36wUoVUrUnkANhp-dnjTdvJgwN82dGQ"
}
其中 kty 字段是必須的,代表密鑰類型,支持 EC 橢圓曲線密鑰,RSA 密鑰和 oct 對稱密鑰。
JWK 和 公鑰格式 Pem 是可以互相轉(zhuǎn)換的:
我們現(xiàn)在已經(jīng)知道,驗證這個 JWT 是需要公鑰或 JWK 的,那你會不會好奇 jwt.io 這個網(wǎng)站是怎么知道 JWK 的呢,為什么一粘貼,就自動將 JWK 填充進去了。
原理其實很簡單,而且已經(jīng)是一種大家都遵循的規(guī)范了,就是將 JWK 放在 iss/.well-known/jwks.json
下,其中 iss 就是 Payload 里面的 iss 。
當你在 jwt.io 粘貼下 JWT 的瞬間,jwt.io 會先解析 Header ,判斷出 JWT 使用的算法(JWA),接著解析出 Payload 的信息,由于這里是 RS256 算法, 所以還會去請求 Payload 里的 iss 下的 .well-known/jwks.json
得到 JWK ,從而完成 JWS 的驗證。
我們說過,經(jīng)過 Signature 簽名后的 JWT 就是指的 JWS ,而 JWS 僅僅是對前兩部分簽名,保證無法篡改,但是其 Payload(載荷) 信息是暴露的(只是作了 base64UrlEncode 處理)。因此,使用 JWS 方式的 Payload 是不適合傳遞敏感數(shù)據(jù)的,JWT 的另一種實現(xiàn) JWE 就是來解決這個問題的。
JWE 全稱是 JSON Web Encryption ( RFC 7516[8] ) ,JWS 的 Payload 是 Base64Url 的明文,而 JWE 的數(shù)據(jù)則是經(jīng)過加密的。它可以使 JWT 更加安全。
JWE 提供了兩種方案:共享密鑰方案和公鑰/私鑰方案。共享密鑰方案的工作原理是讓各方都知道一個密鑰,大家都可以簽名驗證,這和 JWS 是一致的。而公鑰/私鑰方案的工作方式就不同了,在 JWS 中私鑰對令牌進行簽名,持有公鑰的各方只能驗證這些令牌;但在 JWE 中,持有私鑰的一方是唯一可以解密令牌的一方,公鑰持有者可以引入或交換新數(shù)據(jù)然后重新加密,因此,當使用公鑰/私鑰方案時,JWS 和 JWE 是互補的。
想要理解這一點的更簡單的方法是從生產(chǎn)者和消費者的角度進行思考。生產(chǎn)者對數(shù)據(jù)進行簽名或加密,消費者可以對其進行驗證或解密。對于 JWS ,私鑰對 JWT 進行簽名,公鑰用于驗證,也就是生產(chǎn)者持有私鑰,消費者持有公鑰,數(shù)據(jù)流動只能從私鑰持有者到公鑰持有者。相比之下,對于 JWE ,公鑰是用于加密數(shù)據(jù),而私鑰用來解密,在這種情況下,數(shù)據(jù)流動只能從公鑰持有者到私鑰持有者。如下圖所示(來源 JWT Handbook[9] ):
相比于 JWS 的三個部分,JWE 有五個部分組成(四個小數(shù)點隔開)。一個 JWE 示例如下:
eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.
UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_
i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKxYxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8Otv
zlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTPcFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.
AxY8DCtDaGlsbGljb3RoZQ.
KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.
9hH0vgRfYgPnAHOd8stkvw
這五個部分的生成,也就是 JWE 的加密過程可以分為 7 個步驟:
根據(jù) Header alg 的聲明,生成一定大小的隨機數(shù)
根據(jù)密鑰管理方式確定 Content Encryption Key ( CEK )
根據(jù)密鑰管理方式確定 JWE Encrypted Key
計算所選算法所需大小的 Initialization Vector (IV)。如果不需要,可以跳過
如果 Header 聲明了 zip ,則壓縮明文
使用 CEK、IV 和 Additional Authenticated Data ( AAD,額外認證數(shù)據(jù) ) ,通過 Header enc 聲明的算法來加密內(nèi)容,結(jié)果為 Ciphertext 和 Authentication Tag
最后按照以下算法構(gòu)造出 Token:
base64(header) + '.' +
base64(encryptedKey) + '.' + // Steps 2 and 3
base64(initializationVector) + '.' + // Step 4
base64(ciphertext) + '.' + // Step 6
base64(authenticationTag) // Step 6
JWE 相比 JWS 更加安全可靠,但是不夠輕量,有點復(fù)雜。
不管怎樣,JWT 多多少少還是存在一些安全性隱患的,下面是平時開發(fā)過程的一些建議:
始終執(zhí)行算法驗證
簽名算法的驗證固定在后端,不以 JWT 里的算法為標準。假設(shè)每次驗證 JWT ,驗證算法都靠讀取 Header 里面的 alg 屬性來判斷的話,攻擊者只要簽發(fā)一個 "alg: none" 的 JWT ,就可以繞過驗證了。
選擇合適的算法
具體場景選擇合適的算法,例如分布式場景下,建議選擇 RS256 。
HMAC 算法的密鑰安全
除了需要保證密鑰不被泄露之外,密鑰的強度也應(yīng)該重視,防止遭到字典攻擊。
避免敏感信息保存在 JWT 中
JWS 方式下的 JWT 的 Payload 信息是公開的,不能將敏感信息保存在這里,如有需要,請使用 JWE 。
JWT 的有效時間盡量足夠短
JWT 過期時間建議設(shè)置足夠短,過期后重新使用 refresh_token 刷新獲取新的 token 。
今天為大家講了一些 JWT 不為人知的秘密,總結(jié)一下涉及到的知識點:
最后,再次附上 JOSE 的體系圖,相關(guān)的 RFC 均備注在圖上了:
關(guān)鍵字: JWT
文章連接: http://www.hsjyfc.com.cn/cjwt/785.html
版權(quán)聲明:文章由 晨展科技 整理收集,來源于互聯(lián)網(wǎng)或者用戶投稿,如有侵權(quán),請聯(lián)系我們,我們會立即刪除。如轉(zhuǎn)載請保留