你真的瞭解JWT?JWT詳解
引述:
相信很多朋友和我一樣對於現在的登入認證機制都有了一定的認識,並且已經在實施了.我目前接觸到的安全認證是基於Spring Security Oauth2.0。
Oauth2.0的介紹網上有很多的文章,相信有的小夥伴已經看過了,甚至連原始碼都瞭解過了``。我今天說的是在Oauth之上的Token和JWT目前的主流認證模式中,都是使用者輸入使用者名稱和密碼進行登入:
{ loginName: "xxx", password: "xxxxxxx" }
類似這樣的機制,在我經歷過的登入驗證中,我們的處理辦法是使用者登入在本地使用者介面認證成功後呼叫Oauth認證伺服器去進行二次驗證並攜帶基本的client_id和grant_type等必要資訊
這是Oauth的一種授權模式,這樣的流程在成功之後我們可以拿到一個基本的token(基於JWT編碼的)
類似這樣的返回機制:
{ access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWFydmVsLW9wZW4tYXBpIiwibWFydmVsLXZpZGVvLW1vbml0b3IiLCJtYXJ2ZWwtc3ViaXRlbS1zZXJ2ZXIiLCJtYXJ2ZWwtZ3VpZC1zZXJ2ZXIiLCJtYXJ2ZWwtaHItc2VydmVyIiwibWFydmVsLXByb2plY3Qtc2VydmVyIiwibWFydmVsLWF1dGgtc2VydmVyIiwibWFydmVsLXNjcmVlbi1zZXJ2ZXIiLCJtYXJ2ZWwtY29zdC1wYXJlbnQiLCJtYXJ2ZWwtc3VwZXJ2aXNpb24taW5zcGVjdGlvbiIsIm1hcnZlbC1taWRkbGUtc2VydmVyIiwibWFydmVsLXR1bm5lbC1wcm9ncmVzcyIsIm1hcnZlbC1lbmdpbmVlcmluZy1zZXJ2ZXIiLCJtYXJ2ZWwtbWVzc2FnZS1zZXJ2ZXIiXSwiZXhwIjoxNjI3NTc0MTA4LCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6ImFjODg1ZDJiLWRhMTItNGI3Ni1hZjJjLWEzOWFhYjFlNWQ5ZCIsImNsaWVudF9pZCI6Im1pZGRsZSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.aYhu0-J6XzNOzNRNdP5yJJqSXIsgZFABxyLZt2VBNjc" expires_in: 26780 jti: "ac885d2b-da12-4b76-af2c-a39aab1e5d9d" refresh_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWFydmVsLW9wZW4tYXBpIiwibWFydmVsLXZpZGVvLW1vbml0b3IiLCJtYXJ2ZWwtc3ViaXRlbS1zZXJ2ZXIiLCJtYXJ2ZWwtZ3VpZC1zZXJ2ZXIiLCJtYXJ2ZWwtaHItc2VydmVyIiwibWFydmVsLXByb2plY3Qtc2VydmVyIiwibWFydmVsLWF1dGgtc2VydmVyIiwibWFydmVsLXNjcmVlbi1zZXJ2ZXIiLCJtYXJ2ZWwtY29zdC1wYXJlbnQiLCJtYXJ2ZWwtc3VwZXJ2aXNpb24taW5zcGVjdGlvbiIsIm1hcnZlbC1taWRkbGUtc2VydmVyIiwibWFydmVsLXR1bm5lbC1wcm9ncmVzcyIsIm1hcnZlbC1lbmdpbmVlcmluZy1zZXJ2ZXIiLCJtYXJ2ZWwtbWVzc2FnZS1zZXJ2ZXIiXSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiYWM4ODVkMmItZGExMi00Yjc2LWFmMmMtYTM5YWFiMWU1ZDlkIiwiZXhwIjoxNjI5MTY2MTA4LCJqdGkiOiI1YzM1MjQ4OS03ZWEzLTRmMDgtOWExMy03Y2M2MDI2ODhjMDgiLCJjbGllbnRfaWQiOiJtaWRkbGUifQ.uGaynDuGOe8Mh4dOxKAjeUwT_sh1I81_E-UY0UtSGlY" scope: "read write" token_type: "bearer" }
這是一個標準的JWT返回格式的授權Token,
在拿到token後可以放到jwt.io中去解析他的token值
{ "aud": [ "marvel-open-api", "marvel-video-monitor", "marvel-subitem-server", "marvel-guid-server", "marvel-hr-server", "marvel-project-server", "marvel-auth-server", "marvel-screen-server", "marvel-cost-parent", "marvel-supervision-inspection", "marvel-middle-server", "marvel-tunnel-progress", "marvel-engineering-server", "marvel-message-server" ], "exp": 1627574108, "user_name": "admin", "jti": "ac885d2b-da12-4b76-af2c-a39aab1e5d9d", "client_id": "middle", "scope": [ "read", "write" ] }
這是我們解析後得到的值
上面的aud是後端的微服務,每個服務都有各自唯一標識,通過Oauth認證伺服器頒發,有了該標識代表使用者請求具體服務時經過閘道器轉發後不會丟擲401。
這是我目前的使用Jwt的主要流程,但是很多人估計和我一樣完全對於JWT很陌生,只知道他用來編碼封裝token的,對於他沒有去了解,那麼我們一起來看看
JWT描述:
1、JWT簡介:
JSON Web Token(JWT)是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準((RFC 7519),它定義了一種緊湊(Compact)且自包含(Self-contained)的方式,用於在各方之間以JSON物件安全傳輸資訊。 這些資訊可以通過數字簽名進行驗證和信任。 可以使用祕密(使用HMAC演算法)或使用RSA的公鑰/私鑰對對JWT進行簽名。JWT的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該token也可直接被用於認證,也可被加密。是目前最流行的跨域認證解決方案。
Compact(緊湊): 由於它們尺寸較小,JWT可以通過URL,POST引數或HTTP標頭內傳送。 另外,尺寸越小意味著傳輸速度越快。
Self-contained(自包含): 有效載荷(Playload)包含有關使用者的所有必需資訊,避免了多次查詢資料庫。
一個簽名的JWT被稱為JWS (JSON Web簽名)。 事實上,JWT本身並不存在——它必須是JWS或JWE (JSON Web加密)。 它就像一個抽象類——JWS和JWE是具體的實現。
感興趣的深入JWS和JWE的可以閱讀此文章:https://medium.facilelogin.com/jwt-jws-and-jwe-for-not-so-dummies-b63310d201a3
2、JWT應用場景:
Authentication(鑑權): 這是使用JWT最常見的情況。 一旦使用者登入,每個後續請求都將包含JWT,允許使用者訪問該令牌允許的路由,服務和資源。 單點登入是當今廣泛使用JWT的一項功能,因為它的開銷很小,並且能夠輕鬆地跨不同域使用。
分散式站點的單點登入(SSO)
Information Exchange(資訊交換): JSON Web Tokens是在各方之間安全傳輸資訊的好方式。 因為JWT可以簽名:例如使用公鑰/私鑰對,所以可以確定發件人是他們自稱的人。 此外,由於使用標頭和有效載荷計算簽名,因此您還可以驗證內容是否未被篡改。
3、JWT組成:
jwt有3個組成部分,每部分通過點號來分割 header.payload.signature
頭部(header) 是一個 JSON 物件,描述 JWT 的元資料,通常是下面的樣子
載荷(payload) 是一個 JSON 物件,用來存放實際需要傳遞的資料
簽證(signature) 對header和payload使用金鑰進行簽名,防止資料篡改。
① 頭部header
Jwt的頭部是一個JSON,然後使用Base64URL編碼,承載兩部分資訊:
宣告型別typ,表示這個令牌(token)的型別(type),JWT令牌統一寫為JWT
宣告加密的演算法alg,通常直接使用HMACSHA256,就是HS256了,也可以使用RSA,支援很多演算法(HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、ES512、PS256、PS384)
var header = Base64URL({ "alg": "HS256", "typ": "JWT"})
Base64URL:Header 和 Payload 串型化的演算法是 Base64URL。這個演算法跟 Base64 演算法基本類似,但有一些小的不同。JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字元+、/和=,在 URL 裡面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 演算法。
② 載荷payload
payload也是一個JSON字串,是承載訊息具體內容的地方,也需要使用Base64URL編碼,payload中可以包含預定義的7個可用,它們不是強制性的,但推薦使用,也可以新增任意自定義的key
iss(issuer): jwt簽發者
sub(subject): jwt所面向的使用者
aud(audience): 接收jwt的一方, 受眾
exp(expiration time): jwt的過期時間,這個過期時間必須要大於簽發時間
nbf(Not Before): 生效時間,定義在什麼時間之前.
iat(Issued At): jwt的簽發時間
jti(JWT ID): jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
// 該token簽發給1234567890,姓名為John Doe(自定義的欄位),簽發時間為1516239022
var payload = Base64URL( {"sub": "1234567890", "name": "John Doe", "iat": 1516239022})
注意,JWT中payload是不加密的,只是Base64URL編碼一下,任何人拿到都可以進行解碼,所以不要把敏感資訊放到裡面。
③ signature
Signature 部分是對前兩部分的簽名,防止資料篡改。
注意:secret是儲存在伺服器端的,jwt的簽發生成也是在伺服器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了。
4、JWT特點:
因為json的通用性,所以JWT是可以進行跨語言支援的,像JAVA,JavaScript,NodeJS,PHP等很多語言都可以使用。
因為有了payload部分,所以JWT可以在自身儲存一些其他業務邏輯所必要的非敏感資訊。
它不需要在服務端儲存會話資訊, 所以它易於應用的擴充套件
JWT 的幾個特點
(1)JWT 預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用金鑰再加密一次。
(2)JWT 不加密的情況下,不能將敏感資料(如密碼)寫入 JWT,除非對payload進行加密。保護好secret私鑰,該私鑰非常重要。
(3)JWT 不僅可以用於認證,也可以用於交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。
(4)JWT 的最大缺點是,由於伺服器不儲存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的許可權。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。
(5)JWT 本身包含了認證資訊,一旦洩露,任何人都可以獲得該令牌的所有許可權。為了減少盜用,JWT 的有效期應該設定得比較短。對於一些比較重要的許可權,使用時應該再次對使用者進行認證。
(6)為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。
5、JWT優點:
體積小,因而傳輸速度更快
多樣化的傳輸方式,可以通過URL傳輸、POST傳輸、請求頭Header傳輸(常用)
簡單方便,服務端拿到jwt後無需再次查詢資料庫校驗token可用性,也無需進行redis快取校驗
在分散式系統中,很好地解決了單點登入問題
很方便的解決了跨域授權問題,因為跨域無法共享cookie
6、JWT缺點:
因為JWT是無狀態的,因此服務端無法控制已經生成的Token失效,是不可控的,這一點對於是否使用jwt是需要重點考量的
獲取到jwt也就擁有了登入許可權,因此jwt是不可洩露的,網站最好使用https,防止中間攻擊偷取jwt
在退出登入 / 修改密碼時怎樣實現JWT Token失效https://segmentfault.com/q/1010000010043871
7、JWT安全性:
JWT被確實存在被竊取的問題,但是如果能得到別人的token,其實也就相當於能竊取別人的密碼,這其實已經不是JWT安全性的問題。網路是存在多種不安全性的,對於傳統的session登入的方式,如果別人能竊取登入後的sessionID,也就能模擬登入狀態,這和JWT是類似的。為了安全,https加密非常有必要,對於JWT有效時間最好設定短一點。
8、JWT常見問題:
① 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。
9、JWT使用建議:
Payload中的exp時效不要設定太長。
開啟Only Http預防XSS攻擊。
如果擔心重播攻擊(replay attacks )可以增加jti(JWT ID),exp(有效時間) Claim。
在你的應用程式應用層中增加黑名單機制,必要的時候可以進行Block做阻擋(這是針對掉令牌被第三方使用竊取的手動防禦)。
10、JWT與OAuth的區別
OAuth2是一種授權框架 ,JWT是一種認證協議 -無論使用哪種方式切記用HTTPS來保證資料的安全性 -OAuth2用在使用第三方賬號登入的情況(比如使用weibo, qq, github登入某個app),而JWT是用在前後端分離, 需要簡單的對後臺API進行保護時使用。
總結
其實JWT並不在加密保護資料,而是為了認證來源。
JWT不保證資料不洩露,因為JWT的設計目的就不是資料加密和保護。
Angular 的文件中也對於JWT有非常深入的講解,感興趣的同學可以去深入瞭解,地址在下方。
本文引用地址:
JWT文件:https://jwt.io/introduction/
JWT描述:https://www.cnblogs.com/cndarren/p/11518443.html
Angular文件對於JWT瞭解:https://blog.angular-university.io/angular-jwt/
一文說清楚JWT,JWS,JWE:https://www.jianshu.com/p/50ade6f2e4fd
知乎一文說清楚JWT:https://zhuanlan.zhihu.com/p/86937325
JWS和JWE深入:
https://medium.facilelogin.com/jwt-jws-and-jwe-for-not-so-dummies-b63310d201a3,
https://darutk.medium.com/understanding-id-token-5f83f50fa02e