jwt重放攻击_4个点搞懂JWT、JWS、JWE
1.JWT是何物,有哪些常用的場(chǎng)景
JWT(json web token)是設(shè)計(jì)一種簡(jiǎn)潔,安全,無(wú)狀態(tài)的token的實(shí)現(xiàn)規(guī)范rfc7519,通常用于網(wǎng)絡(luò)請(qǐng)求方和網(wǎng)絡(luò)接收方之間的網(wǎng)絡(luò)請(qǐng)求認(rèn)證。
jwt的常用場(chǎng)景
1.1: restful api接口的無(wú)狀態(tài)認(rèn)證, 在傳統(tǒng)的web應(yīng)用中,我們通常采用session認(rèn)證。
session認(rèn)證流程:
客戶端將用戶名/密碼通過(guò)某種加密的方式發(fā)送給服務(wù)器
服務(wù)器接收到客戶端請(qǐng)求之后進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)后使用Set-Cookie將用戶的唯一sessionid放入到cookie當(dāng)中,并將生成的sessionid和用戶的關(guān)聯(lián)信息存入到內(nèi)存。
客戶端第二次訪問(wèn)后服務(wù)器從當(dāng)前cookie中取出sessionid并從內(nèi)存中拿到相同的sessionid. 如果不存在,或者沒(méi)有攜帶sessionid則說(shuō)明該用戶登陸過(guò)期,或者未登陸。
session認(rèn)證的一些缺點(diǎn):
由于使用session進(jìn)行認(rèn)證的方式必須存儲(chǔ)sessionid,當(dāng)用戶量過(guò)大時(shí)對(duì)服務(wù)器內(nèi)存消耗影響巨大. 如果你沒(méi)有設(shè)置session的過(guò)期時(shí)間(關(guān)閉瀏覽器并不會(huì)導(dǎo)致cookie消失)那么對(duì)你的服務(wù)器來(lái)說(shuō)消耗是致命的。
如果你沒(méi)有將session存在一個(gè)所有服務(wù)器都可以獲取得到的地方如redis, 那么意味著在本臺(tái)服務(wù)器上面存儲(chǔ)的sessionid其他服務(wù)器無(wú)法獲取。用戶進(jìn)行請(qǐng)求時(shí)必須請(qǐng)求到這臺(tái)服務(wù)器上面。可擴(kuò)展性較差。
跨平臺(tái)性較差,傳統(tǒng)的session認(rèn)證方式在移動(dòng)端很難行得通。你必須開(kāi)發(fā)二套不同的邏輯對(duì)web和移動(dòng)端進(jìn)行認(rèn)證。
jwt認(rèn)證流程:
客戶端將用戶名/密碼通過(guò)某種加密的方式發(fā)送給服務(wù)器。
服務(wù)器接收到客戶端請(qǐng)求后進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)服務(wù)器生成token返回給客戶端。客戶端將token存儲(chǔ)在本地。
客戶端每次請(qǐng)求將token攜帶在http header頭中, 服務(wù)器端將token取出進(jìn)行解密。
jwt認(rèn)證的優(yōu)點(diǎn):
服務(wù)器端無(wú)需保存token,以加解密的方式代替存儲(chǔ),節(jié)省了內(nèi)存空間。
無(wú)狀態(tài)的token不依賴于服務(wù)器保存會(huì)話信息,更利于水平擴(kuò)展。
相比于傳統(tǒng)的session認(rèn)證方式,jwt對(duì)移動(dòng)端的支持更友好。
可以看出jwt認(rèn)證解決了傳統(tǒng)的session認(rèn)證的一些不足之處。
1.2: 一次性認(rèn)證:
如需要對(duì)某一個(gè)應(yīng)用進(jìn)行授權(quán)使用,此時(shí)就可以將jwt進(jìn)行攜帶訪問(wèn)。
2.jwt的組成和生成方式
2.1jwt主要由三個(gè)部分組成,分別是頭部(header),載荷(payload),簽名(signature)組成。
header:
頭部是用來(lái)聲明此jwt的類型和加密算法,它們通常由alg和typ這二個(gè)字段組成。
alg字段通常用于表示加密采用的算法。
typ字段通常用于表示類型。
payload:
載荷就是我們存放公共參數(shù)/私有參數(shù)的地方.通俗點(diǎn)說(shuō)該字段就是存放系統(tǒng)中用戶的信息和jwt本身的一些信息,rfc文檔本身替我們提供了一組字段的聲明 (Claims)
iss: 該字段表示jwt的簽發(fā)者。可以用你的應(yīng)用唯一標(biāo)識(shí)或者高權(quán)限的userid填充此字段。
sub: 該jwt面向的用戶。
aud: jwt的接收方。
exp: jwt的過(guò)期時(shí)間,通常來(lái)說(shuō)是一個(gè)時(shí)間戳。
iat: jwt的簽發(fā)時(shí)間,常來(lái)說(shuō)是一個(gè)時(shí)間戳。
jti:此jwt的唯一標(biāo)識(shí)。通常用于解決請(qǐng)求中的重放攻擊。該字段在大多數(shù)地方?jīng)]有被提及或使用。因?yàn)槭褂么俗侄尉鸵馕吨仨氁诜?wù)器維護(hù)一張jti表, 當(dāng)客戶端攜帶jwt訪問(wèn)的時(shí)候需要在jti表中查找這個(gè)唯一標(biāo)識(shí)是否被使用過(guò)。使用這種方式防止重放攻擊似乎讓jwt有點(diǎn)怪怪的感覺(jué), 畢竟jwt所宣稱的優(yōu)點(diǎn)就是無(wú)狀態(tài)訪問(wèn)。-.-
{
????"iss":?"appid_xxxxxx"
????"sub":?"012345122",
????"exp":?"1572246721840",
????"iat":?"1592246721840"
}
signature簽名流程:
1.首先將header和payload進(jìn)行base64編碼,然后使用"."將header和payload拼接起來(lái)。類似于像下面這樣:
String?base64data?=?Base64.encode(header)+"."+Base64.encode(payload)2.在將payload和header進(jìn)行base64之后進(jìn)行簽名,得到簽名后的數(shù)據(jù)。簽名所使用的算法來(lái)自于header頭中的alg字段。簽名過(guò)程類似于像下面這樣:
????final?JWSSigner?signer?=?new?ECDSASigner(this.privateKey);?????????????this.signature?=?signer.sign(base64data);
????????????//第一步,?實(shí)例化一個(gè)簽名對(duì)象
????????????//第二步,對(duì)base64data?進(jìn)行簽名
3.簽名之后將簽名的值和base64之后的header和payload用"."號(hào)連接起來(lái)。此時(shí)一個(gè)完整的jwt就出來(lái)啦。
jwt的最終結(jié)構(gòu): header.payload.signature
3.使用jwe來(lái)使你的jwt更加安全
簽名到底在干什么:
在上面我們所談到的jwt僅僅是簽名后的jwt。在這里我們需要明白一個(gè)概念,那就是簽名并不能保證數(shù)據(jù)的安全,也就是說(shuō)如果有人獲取到了你的jwt那么他可以通過(guò)轉(zhuǎn)碼得到你jwt當(dāng)中所有的信息。那么讀者可能會(huì)有點(diǎn)想罵人了,你特么上面說(shuō)了那么多連一個(gè)數(shù)據(jù)安全都不能保證那么我看那么多有啥作用- -. 別急,我們先來(lái)了解一下簽名的概念。其實(shí)對(duì)于簽名更專業(yè)點(diǎn)的來(lái)說(shuō)就是進(jìn)行了一次哈希散列,對(duì)于散列我們首先要保證一下四點(diǎn)概念.
相同的輸入將始終產(chǎn)生相同的輸出。
多個(gè)不同的輸入不應(yīng)產(chǎn)生相同的輸出。
從輸出到輸入應(yīng)該是不可能的。
給定輸入的任何修改都將導(dǎo)致哈希值發(fā)生巨大變化。
從上面4點(diǎn)大家看明白了嗎?哈希與身份驗(yàn)證結(jié)合使用,可以產(chǎn)生強(qiáng)有力的證據(jù)來(lái)證明給定的消息尚未被修改。也就是說(shuō)這個(gè)jwt從我這里簽發(fā)以后無(wú)法改變,從而保證了數(shù)據(jù)來(lái)源的可靠性。
當(dāng)客戶端攜帶jwt進(jìn)行請(qǐng)求時(shí)服務(wù)器在執(zhí)行一遍簽名步驟。如果簽名的值一樣就表示這個(gè)jwt是可靠的。此時(shí)我們?cè)诳蛻舳撕头?wù)器之間就建立一種可信任的token機(jī)制。
應(yīng)該怎樣保證數(shù)據(jù)安全:
對(duì)于如何保證jwt本身的數(shù)據(jù)安全很多文章或文檔都可以提及,我們可以把上面所生成的jwt成為jws(JSON Web Signed). 他本身的數(shù)據(jù)并沒(méi)有進(jìn)行加密。
此時(shí)如果我們想保證數(shù)據(jù)的安全就需要使用jwe(JSON Web Encryption)對(duì)jwt進(jìn)行加密。jwe加密的秘文如下所示
// jwe相對(duì)于jws來(lái)說(shuō)多了二個(gè)組成部分。eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.
?????OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe
?????ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb
?????Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV
?????mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8
?????1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi
?????6UklfCpIMfIjf7iGdXKHzg.
?????48V1_ALb6US04U3b.
?????5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji
?????SdiwkIr3ajwQzaBtQD_A.
?????XFBoMYUZodetZdvTiFvSkQ
jwe的5個(gè)組成部分:
JWE header: 描述用于創(chuàng)建jwe加密密鑰和jwe密文的加密操作,類似于jws中的header。參數(shù)不一一描述,詳情請(qǐng)見(jiàn)jwe header參數(shù)
JWE Encrypted Key:用來(lái)加密文本內(nèi)容所采用的算法。
JWE initialization vector: 加密明文時(shí)使用的初始化向量值,有些加密方式需要額外的或者隨機(jī)的數(shù)據(jù)。這個(gè)參數(shù)是可選的。
JWE Ciphertext:明文加密后產(chǎn)生的密文值。
JWE Authentication Tag:數(shù)字認(rèn)證標(biāo)簽。
??{
??????"protected":"jwe受保護(hù)的header頭",
??????"unprotected":"JWE?Shared?Unprotected?Header數(shù)據(jù)",
??????"header":"",
??????"encrypted_key":"密鑰加密后數(shù)據(jù)????",
??????"aad":"額外的認(rèn)證數(shù)據(jù)",
??????"iv":"同上的?JWE?initialization?vector",
??????"ciphertext":"同上的JWE?Ciphertext",
??????"tag":"同上的JWE?Authentication?Tag"
?????}
jwe創(chuàng)建流程:
根據(jù)頭部 alg 的聲明,將header頭進(jìn)行編碼
隨機(jī)生成密鑰
加密密鑰
生成iv如果不需要,此步驟可以省略.
加密原始報(bào)文
生成認(rèn)證算法得到Authentication Tag.
如果明文有聲明zip壓縮,那么壓縮明文
4. jwt的缺點(diǎn)以及常見(jiàn)的理解誤區(qū)
缺點(diǎn):
無(wú)法主動(dòng)的過(guò)期token. 常見(jiàn)的場(chǎng)景為后臺(tái)踢出用戶或封禁用戶,此時(shí)若是token還在生效
時(shí)間范圍內(nèi),那么意味著該用戶在被踢出系統(tǒng)后還可以在這個(gè)時(shí)間范圍內(nèi)進(jìn)行訪問(wèn)。又或者用戶修改了密碼,此時(shí)原token依然可以進(jìn)行加解密也就代表著用戶仍然可以繼續(xù)訪問(wèn)。更為致命的是大多數(shù)客戶端會(huì)將token存放在Local Storage或者vuex中,這意味著除非用戶點(diǎn)擊退出登陸。否則就算關(guān)閉軟件在重新打開(kāi)也會(huì)造成原密碼可以登陸的假象。jwt對(duì)比于傳統(tǒng)的session認(rèn)證方案并不會(huì)提高運(yùn)行效率,因?yàn)楸举|(zhì)上jwt做的是一個(gè)以時(shí)間換空間的動(dòng)作。頻繁的加解密會(huì)帶來(lái)不小的性能開(kāi)銷。
常見(jiàn)的理解誤區(qū):
對(duì)jwt進(jìn)行存儲(chǔ),這個(gè)是最常見(jiàn)的理解誤區(qū)。事實(shí)上很多人都覺(jué)得將jwt進(jìn)行存儲(chǔ)可以完美的解決主動(dòng)過(guò)期的問(wèn)題, 然而這是一種賠了夫人又損兵的做法。既無(wú)法節(jié)省空間也無(wú)法節(jié)省時(shí)間。如果你的應(yīng)用必須要主動(dòng)過(guò)期的功能,那么我推薦你使用傳統(tǒng)的session,事實(shí)上傳統(tǒng)的session也有不少成熟的解決方案。如spring-session等。
jwt被盜用會(huì)導(dǎo)致數(shù)據(jù)泄漏不安全, 事實(shí)上使用jwe加密的jwt是不存在數(shù)據(jù)不安全的問(wèn)題的。第二,jwt的數(shù)據(jù)一般都是非敏感數(shù)據(jù),由于簽名機(jī)制的存在所以你盜用了jwt做不了任何事情。
不存儲(chǔ)如何實(shí)現(xiàn)退出功能。直接在客戶端清除token就行了。因?yàn)榉?wù)器不存儲(chǔ)所以只需要在客戶端清除token就可以做到用戶退出。
將token的時(shí)期設(shè)計(jì)的非常長(zhǎng)。這是一個(gè)會(huì)造成很多隱性問(wèn)題的設(shè)計(jì),比如上述的密碼修改等問(wèn)題。這個(gè)時(shí)候可以參見(jiàn)oauth2的做法,將token的過(guò)期時(shí)間設(shè)置為一小時(shí),但是增加一個(gè)refreshToken。客戶端在沒(méi)有觸發(fā)退出或者修改密碼等操作時(shí)通過(guò)refreshToken來(lái)刷新token。
?你在看嗎?
總結(jié)
以上是生活随笔為你收集整理的jwt重放攻击_4个点搞懂JWT、JWS、JWE的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 学习笔记_jquery(js)遍历页面标
- 下一篇: discuz安装_手动搭建 Discuz