JSON Web Token(JWT)機制學習

零、先從傳統身份認證說起

HTTP是一種無狀態的協議,也就是它並不知道誰是訪問應用。把用戶看成是客戶端,客戶端使用用戶名還有密碼通過了身份認證,不過下回這個客戶端再發送請求的時候,服務端還得再驗證一下,這就很麻煩。

對於這個情況的解決方法是:
當用戶請求登錄的時候,如果沒有問題,我們在服務器端生成一條記錄,這個記錄裏可以說明一下登錄的用戶是誰,然後把這條記錄的ID號發送給客戶端,客戶端收到以後把這個ID號存儲在cookie裏,下次這個用戶再向服務器發送請求的時候,可以帶着這個cookie,這樣服務端會驗證一個這個cookie裏的信息,看看能不能在服務器端這裏找到對應的記錄,如果可以,說明用戶已經通過了身份驗證,就把用戶請求的數據返回給客戶端。

其實這個解決方法就是session,我們需要在服務端存儲爲登錄的用戶生成的session,這些session可能會存儲在內存、磁盤或者數據庫裏。我們需要定期清理過期的session。

這種傳統身份認證的問題是:

  1. Session:每次認證用戶發起請求時,服務器需要去創建一個記錄才存儲信息。當越來越多的用戶發請求時,內存的開銷也會不斷增加。
  2. CORS(跨域資源共享):當我們需要讓數據跨多臺移動設備上使用時,跨域資源的共享會是一個問題。在使用Ajax抓取另一個域的資源,就可以會出現禁止請求的情況。
  3. CSRF(跨站請求僞造)攻擊:用戶在訪問銀行網站時,他們很容易受到跨站請求僞造的攻擊,並且能夠被利用其訪問其他的網站。

一、Token的身份認證機制

這種身份認證的好處就是在服務器中不需要存儲用戶的登錄記錄。而且具有無狀態、可擴展的特點。在客戶端存儲的tokens是無狀態的,並且能夠被擴展。基於這種無狀態和不存儲的session信息,負載均衡器能夠將用戶信息從一個服務器傳到其他服務器上。
並且token身份認證是在請求中發送token而不再是發送cookie,這樣做能夠防止CSRF(跨站請求僞造)。即使在客戶端使用cookie存儲token,cookie也僅僅是一個存儲機制而不是用於認證。

二、Token身份認證的大概流程

  1. 客戶端使用用戶名和密碼請求登錄
  2. 服務端收到請求,然後對用戶名和密碼進行驗證
  3. 驗證成功後,服務端會簽發一個Token(令牌,具有唯一性),再把這個Token發送給客戶端
  4. 客戶端收到Token以後可以把它存儲起來,比如放在cookie裏或者Local Storage裏
  5. 客戶端每次向服務端請求資源的時候需要帶着服務端簽發的Token
  6. 服務端收到請求,然後去驗證客戶端請求裏面帶着的Token,如果驗證成功,就向客戶端返回請求的數據

三、JSON Web Token(JWT)機制

JWT是一種緊湊且自包含的,用於在多方傳遞JSON對象的技術。傳遞的數據可以使用數字簽名增加其安全行。可以使用HMAC加密算法或RSA公鑰/私鑰加密方式。

緊湊:數據小,可以通過URL、POST參數,請求頭髮送。且數據小代表傳輸速度快。
自包含:使用payload數據塊記錄用戶必要且不隱私的數據,可以有效的減少數據庫訪問次數,提高代碼性能。

JWT一般用於處理用戶身份驗證或數據信息交換

用戶身份驗證(鑑權):一旦用戶登錄,每個後續請求都將包含JWT,允許用戶訪問該令牌允許的路由、服務和資源。單點登錄是當今廣泛使用JWT的一項功能,因爲它開銷很小,並且能夠輕鬆的跨不同域使用。
數據信息交換:JWT是一種非常方便的多方傳遞數據的載體,因爲其可以使用數據簽名來保證數據的有效性和安全性。例如使用公鑰私鑰,可以確定發件人是他們自稱的人。此外,由於使用標頭和有效載荷計算簽名,還可以驗證內容是否未被篡改。

四、JWT的數據結構

JWT的數據結構是:A.B.C
由字符 . 來分隔三部分數據。
A - header頭信息(類似請求頭)不能傳空數據
B - payload有效荷載(記錄用戶的有效信息,避免多次訪問數據庫) 可以傳空數據,但一般建議包含
C - Signature簽名(header+payload+密匙 組成的一個密文數據) 不能傳空數據


header(頭信息)
大體上是一個JSON數據對象,有兩部分組成,常用的散列算法以及令牌的類型。

數據結構:{"alg":"加密算法名稱","typ":"JWT"}

alg是加密算法定義內容,如HMAC SHA256 或 RSA
typ是token類型,這裏固定爲JWT

header部分的JSON被Base64Url編碼,形成JWT的第一部分。


payload(有效荷載)
在payload數據塊中一般用於記錄實體(通常爲用戶信息)或其他數據的。主要分爲是三個部分:已註冊聲明(registered claims),公開聲明(public claims),私有聲明(private claims)
payload中常有信息有:iss(發行者),exp(到期時間),sub(主題),aud(受衆)等,這些都是已註冊聲明。
數據結構:{"sub":"125767","name":"DAMN","admin":true}
公開聲明部分一般都會在JWT註冊表中增加定義,但是爲了避免和已註冊信息衝突,應在IANA JSON Web令牌註冊表中定義它們,或將其定義爲包含防衝突命名空間的URI。
私有聲明是爲了同意使用既沒有登記,也沒有公開聲明各方之間的共享信息而創建的定製聲明。
公開聲明和私有聲明可以由程序員任意定義。

payload部分的JSON被Base64Url編碼,形成JWT的第二部分。

注意:即使JWT有簽名加密機制,但是payload內容都是明文記錄,除非記錄的是加密數據,否則不排除泄露隱私數據的可能,不推薦在payload中記錄任何敏感數據。


signature(簽名信息)
簽名信息,是由一個開發者提供的信息,是服務器驗證的傳遞的數據是否有效安全的標準。在生成JWT最終數據之前,先使用header中定義的加密算法,將header和payload進行加密,並且使用 . 進行連接。用來驗證發送請求者的身份,由前兩部分加密形成,要創建前面部分,必須採用編碼標頭,編碼有效載荷,祕鑰,標頭中指定的算法並簽名。如:加密後的header與加密後的payload,在使用相同的加密算法,對加密後的數據和signature進行加密。得到最終結果。

五、JWT的工作原理

在身份驗證中,當用戶使用他們的憑證成功登錄時,JWT將被返回並且必須保存在本地(通常在本地存儲中,也可以使用cookie)。無論何時用戶想要訪問受保護的路由或資源,用戶代理都應該使用承載方案發送JWT,通常在請求頭中的Authorization字段,使用Bearer schema:Authorization:Bearer <token>
這是一種無狀態身份驗證機制,因爲用戶狀態永遠不會保存在服務器內存中。服務器受保護的路由將在授權頭中檢查有效的JWT,如果在服務端存在,則允許用戶訪問受保護的資源。由於JWT是獨立的,所有必要的信息都在那裏,減少了多次查詢數據庫的需求。
ps:JWT是基於瀏覽器來開發的

六、常見問題

1.JWT安全嗎?
答:Base64編碼的方式是可逆的,也就是透過編碼後發放的Token內容是可以被解析的。一般不建議在payload內放敏感信息。
2.JWT payload內容可以被僞造嗎?
答:JWT其中的一個組成內容爲Signature,可以防止通過Base64可逆方法回推payload內容並且將其修改,因爲Signature是經過header和payload一起Base64編碼組成的。
3.如果cookie被竊取了,那麼第三方可以做CSRF攻擊嗎?
答:是的,cookie丟失,就表示身份可以被僞造,官方建議的使用方式是存放在LocalStorage中,並放在請求頭中發送。
4.空間及長度問題?
答:JWT Token通常長度不會太端,特別是無狀態令牌(Stateless JWT Token),可如果把所有的數據都編在Token裏,很快的就會超過Cookie的大小(4K)或者是URL長度限制。
5.Token的失效問題?
答:無狀態JWT令牌發放出去之後,不能通過服務器端讓令牌失效,必須等到過期時間纔會失去效用。假設在這之間Token被攔截,或者有權限管理員身份的差異造成授權Scope修改,這也不能阻止發出去的Token失效以及要求使用使用者重新請求新的Token。

七、對JWT機制的使用建議

  1. 不要存放敏感信息在Token裏;
  2. payload中的exp(有效時間)時效不要設定太長;
  3. 開啓Only Http以預防XSS攻擊;
  4. 在你的應用程序應用層中增加黑名單機制,必要的時候可以進行Block做阻擋。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章