JSON Web Tokens(JWT)的理解

前言

雖然前兩年系統(tǒng)中就已經(jīng)應(yīng)用了JWT作為token用來(lái)鑒權(quán)認(rèn)證等,大概知道那么回事,但是經(jīng)常交流過(guò)程中,面對(duì)別人的疑問(wèn)沒(méi)法給一個(gè)深入的解答,所以重新梳理了下JWT相關(guān)知識(shí)。

1.什么是JWT?

JWT官網(wǎng)
JSON Web Token (JWT)是一個(gè)開(kāi)放標(biāo)準(zhǔn)(RFC 7519),它定義了一種緊湊且自包含的方式,以JSON對(duì)象的方式在各方之間安全地傳輸信息,因?yàn)槭腔跀?shù)字簽名所以可以進(jìn)行驗(yàn)證和信任,常用的算法比如有HMAC、RSA等等。

2.JWT數(shù)據(jù)結(jié)構(gòu)

JWT由三部分組成,以"點(diǎn)"(.)作為分隔符,看起來(lái)格式這樣的xxx.yyy.zzz

接下來(lái)看一下這三部分的具體構(gòu)成:

  • Header
    Header是JWT的第一部分,包含算法類(lèi)型和token類(lèi)型的說(shuō)明,舉例如下,算法是用的HS256指的是HMACSHA256,token類(lèi)型是JWT。
    HMACSHA256不了解?可以看看對(duì)算法介紹的這一篇密碼學(xué)相關(guān)知識(shí)梳理
    Base64Url不了解?Base64和Base64Url區(qū)別是什么?下文會(huì)對(duì)Base64和Base64URL做一些簡(jiǎn)單介紹。
例子:Header內(nèi)容
{
 "alg": "HS256",
 "typ": "JWT"
}
對(duì)該json進(jìn)行Base64Url編碼得到
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9,也就是xxx.yyy.zzz的xxx部分的值。
  • Payload:載荷
    Payload是JWT第二部分,我們把數(shù)據(jù)放在這部分,舉例如下
例子:Payload內(nèi)容
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
對(duì)該json進(jìn)行Base64Url編碼得到
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
  • Signature:簽名
    Signature是JWT第三部分,是對(duì)前兩塊內(nèi)容的簽名值,比如以簽名算法HMACSHA256為例
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
按照上面計(jì)算規(guī)則:
 base64UrlEncode(header) + "."+base64UrlEncode(payload)=xxx.yyy
也就是說(shuō)第三部分的值就是對(duì)“xxx.yyy”進(jìn)行HMACSHA256摘要計(jì)算,這里secret假設(shè)等于“123456789”
計(jì)算出來(lái)的第三部分值:S2ZL7D-D3VeduQ44Cy2qLRFxHV43gRGSZtlfJ2MJ57g

最后把上面三部分拼接到一起組成完整的JWT如下,從上面的計(jì)算規(guī)則我們可以發(fā)現(xiàn),JWT的安全性其實(shí)就是基于算法的原理,比如用的HMACSHA256,那么密鑰就是安全的保證,別人拿不到密鑰就無(wú)法偽造,無(wú)法篡改JWT。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.S2ZL7D-D3VeduQ44Cy2qLRFxHV43gRGSZtlfJ2MJ57g

2.1一些理解:

  • Q1:Payload是否可以存敏感數(shù)據(jù)?
  • A1:不能,Header和Payload都只是對(duì)數(shù)據(jù)進(jìn)行Base64Url編碼而已,了解Base64的話,應(yīng)該知道這個(gè)不是加密,第三方拿到編碼數(shù)據(jù)可以直接解碼,所以不要放任何敏感數(shù)據(jù),除非額外對(duì)數(shù)據(jù)又進(jìn)行加密處理。
  • Q2:第三方是否可以偽造JWT?是否可以篡改Payload里面的數(shù)據(jù)?
  • A2:從上面第三部分Signature簽名值計(jì)算規(guī)則,我們可以看到,是用HMACSHA256算法以及密鑰key計(jì)算Header和Payload的摘要值,密鑰key是服務(wù)端私有的,理論上攻擊者沒(méi)有密鑰key就無(wú)法生成出一樣的簽名值,所以結(jié)論是無(wú)法偽造jwt,同樣的篡改Payload里面的數(shù)據(jù),服務(wù)端計(jì)算出來(lái)的簽名值就會(huì)不匹配,這樣就可以認(rèn)為被篡改了,直接拒絕服務(wù),所以密鑰key是關(guān)鍵,不能泄露。
  • Q3:假設(shè)如下截圖中消息是xxx.yyy的值,HMACSHA256簽名后在進(jìn)行Base64Url編碼得到的是如下截圖中的結(jié)果A'還是結(jié)果B?
  • A3:答案是B,詳細(xì)解釋如下:
    如下截圖中消息是base64UrlEncode(header) + "." +base64UrlEncode(payload),也就是待簽名內(nèi)容,使用密鑰key通過(guò)計(jì)算,結(jié)果A是HMACSHA256計(jì)算的摘要值字節(jié)數(shù)組轉(zhuǎn)十六制的值64個(gè)字符(sha256計(jì)算摘要值是256bit,1個(gè)字節(jié)8bit,256bit對(duì)應(yīng)32個(gè)字節(jié),1個(gè)字節(jié)用2位16進(jìn)制表示,所以結(jié)果A是64位),然后對(duì)結(jié)果A進(jìn)行Base編碼得到結(jié)果A' 88個(gè)字符(64位十六進(jìn)制讀取的時(shí)候每一位當(dāng)做一個(gè)字符讀?。?結(jié)果B是對(duì)HMACSHA256計(jì)算出來(lái)的32個(gè)字節(jié)原始二進(jìn)制直接進(jìn)行Base64編碼得到的是44個(gè)字符,結(jié)果B是才是正確的流程,結(jié)果A為什么會(huì)長(zhǎng)度多一倍呢?因?yàn)楹灻?2個(gè)字節(jié)轉(zhuǎn)成16進(jìn)制后,在計(jì)算Base64的讀取十六進(jìn)制的時(shí)候是以字符讀取,變成了64個(gè)字節(jié),一出一進(jìn)長(zhǎng)度翻了一倍。

3.Base64和Base64Url

3.1Base64算法

Base64主要是對(duì)給定的字符和字符編碼(如ASCII碼,UTF-8碼)對(duì)應(yīng)的十進(jìn)制數(shù)為基準(zhǔn),做編碼操作:
1.將給定的字符串以字符為單位轉(zhuǎn)換為對(duì)應(yīng)的字符編碼(如ASCII碼)
2.將獲得的字符編碼轉(zhuǎn)換為二進(jìn)制碼
3.對(duì)獲得的二進(jìn)制碼做分組轉(zhuǎn)換操作,每3個(gè)8位二進(jìn)制碼為一組,轉(zhuǎn)換為每4個(gè)6位二進(jìn)制碼為一組(不足6位時(shí)地位補(bǔ)0),這是一個(gè)分組變化的過(guò)程,3個(gè)8位二進(jìn)制碼和4個(gè)6位二進(jìn)制碼的長(zhǎng)度都是24位(38=46=24)
4.對(duì)獲得的4個(gè)6位二進(jìn)制碼補(bǔ)位,向6位二進(jìn)制碼添加2位高位0,組成4個(gè)8位二進(jìn)制碼
5.將獲得的4個(gè)8位二進(jìn)制碼轉(zhuǎn)換為十進(jìn)制碼
6.將獲得的十進(jìn)制碼轉(zhuǎn)換為Base64字符表中對(duì)應(yīng)的字符
舉例如下:ASCII碼字符編碼
對(duì)字符串“A”進(jìn)行Base64編碼,如下表示:

字符                     A
ASCII碼                  65
二進(jìn)制碼                 01000001
4-6二進(jìn)制碼              010000      010000
4-8二進(jìn)制碼              00010000    00010000
十進(jìn)制碼                 16          16
字符表映射碼              Q          Q           =       =

字符“A‘經(jīng)過(guò)Base64編碼后得到"QQ=="這樣一個(gè)字符串,Base64是以4個(gè)字符為單位,其長(zhǎng)度只能是4個(gè)字符的整數(shù)倍,不滿足時(shí)就用等號(hào)補(bǔ)位。

3.2 Base64Url

查看Base64字符映射表我們可以知道有一些字符比如"/"、“+”、“=”,像這種字符在Url中有特殊的意義,所以我們需要替代這些字符,替代規(guī)則是“-”替代“+”,"_"替代“/”,"="直接去掉,這就是Base64Url編碼方式。

  • Base64Url編碼的流程
1.明文使用BASE64進(jìn)行編碼
2. 在BASE64的基礎(chǔ)上進(jìn)行以下的編碼:
   去除尾部的"="
   把"+"替換成"-"
   把"/"替換成"_"
  • Base64Url解碼的流程
1.替換字符
    把"-"替換成"+"
    把"_"替換成"/"
   (計(jì)算Base64Url編碼長(zhǎng)度)%4
    結(jié)果為0,不做處理
    結(jié)果為2,字符串末尾添加"=="
    結(jié)果為3,字符串末尾添加"="
 2、使用Base64解碼密文,得到原始的明文

3.2.1Base64Url解碼問(wèn)題

交流的時(shí)候有個(gè)同事剛好提出為什么補(bǔ)等號(hào)的時(shí)候, (計(jì)算Base64Url編碼長(zhǎng)度)%4結(jié)果為0,2,3,沒(méi)有為1的情況?
接下來(lái)看一下這個(gè)計(jì)算過(guò)程,前面提到Base64編碼規(guī)則每3個(gè)8位二進(jìn)制碼為一組,轉(zhuǎn)換為每4個(gè)6位二進(jìn)制碼為一組(不足6位時(shí)地位補(bǔ)0),假設(shè)我們對(duì)一組字節(jié)編碼,比如22個(gè)字節(jié),每三個(gè)字節(jié)一組,最后剩下一個(gè)字節(jié)(22mod3=1),23個(gè)字節(jié)分組后剩下2個(gè)字節(jié)(23mod3=2),21個(gè)字節(jié)則正好分成7組,所以分組過(guò)程中只可能剩1個(gè)字節(jié)或者2個(gè)字節(jié),下面對(duì)剩1個(gè)和2個(gè)字節(jié)的分析

  • 1個(gè)字節(jié)
    從前面對(duì)字符"A"編碼過(guò)程也可以看到,如果一個(gè)字節(jié)比如01000001,拆成4-6二進(jìn)制的時(shí)候是010000和010000(本來(lái)是拆成6位010000和01,不足6位的補(bǔ)0),4-8二進(jìn)制變成00010000和00010000,然后在轉(zhuǎn)10進(jìn)制然后從Base64映射對(duì)應(yīng)的字符得到兩個(gè)字符QQ,Base64是4個(gè)字符位單位,所以補(bǔ)兩個(gè)"=="。
  • 2個(gè)字節(jié)
    從上面剩1個(gè)字節(jié)的分析可以很容易的看出,2個(gè)字節(jié)拆成4-6二進(jìn)制的時(shí)候?qū)?yīng)3個(gè)4-6,4-8二進(jìn)制也是3個(gè),最后得到字符3個(gè),補(bǔ)一個(gè)“=”號(hào)。
    所以轉(zhuǎn)成Base64后,可能剛好分組,或補(bǔ)1個(gè)等號(hào)或2個(gè)等號(hào)這三種,不存在補(bǔ)3個(gè)等號(hào)的。

3.3擴(kuò)展的一些思考

我們將JWT用作系統(tǒng)token機(jī)制,用戶(hù)登錄后服務(wù)端頒發(fā)token,后續(xù)請(qǐng)求頭帶上token,服務(wù)端檢驗(yàn)該token是否合法來(lái)判斷用戶(hù)的合法性。

  • 對(duì)于現(xiàn)在很多單頁(yè)面應(yīng)用比如基于AngluarJS,Vue,拿到token后存在了localstorage中,后續(xù)請(qǐng)求在從localstorage中取出,這樣是否安全?是否有更好的方案?
  • 我們?cè)谝郧皃c時(shí)代通過(guò)session,cookie機(jī)制進(jìn)行會(huì)話跟蹤,那這兩種機(jī)制本質(zhì)上的差別是什么?
    這兩個(gè)問(wèn)題因?yàn)檫€沒(méi)有完全想明白也沒(méi)有一個(gè)完美的解決方案,所以暫時(shí)拋出來(lái)記錄一下。

4.附錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容