您現在的位置是:首頁 > 動作武俠首頁動作武俠
如何做好網路安全中的登入憑證(Cookie-session VS JWT)
- 2022-02-01
令牌已經超時失效什麼意思
在現代網路環境中,無論是web應用,還是客戶端app,都無法離開登入的功能,相信接觸到的專案中或多或少都會有用到它,差別就是你在它上面有多少了解。為了儘快完成專案,登入這塊會做的簡單,使用者名稱密碼驗證,稍微複雜一點,注意敏感資訊的,會把密碼做一次hash存入DB,這很好,我相信如果你做到了這一點,那麼你對於安全已經有了一個最基本的思維了,那麼我們還可以做什麼,密碼加鹽我相信你也聽過,為了防止彩虹攻擊,弱密碼的原因,我們採用加鹽演算法,可以有效的防止這種攻擊,那麼我們還能做什麼?感興趣的話我會在下一講提到,可以關注一下,我想一定會對你有所幫助。
今天我們講的是更進一步,在你登陸完以後,我們如何生成以及驗證憑證。
我們會分幾點講,第一點是新人在開發中很多都是單機的情況下,如果選擇完善自己的憑證,還有一點是擴充套件,如果你已經又進一步的考慮到了服務叢集,那麼我們又如何選擇呢?
HTTP協議中最傳統的狀態管理機制,Cookie-Session
HTTP無狀態,但是認證授權的時候,我們需要伺服器有辦法來區分出使用者。
現成的方案是HTTP協議中增加了Set-Cookie指令,這個指令的含義是以鍵值對的方式向客戶端傳送一組資訊,在此後一段時間內的每次 HTTP 請求中,這組資訊會附帶著名為 Cookie 的 Header 重新發回給服務端,以便伺服器區分來自不同客戶端的請求。
下面就是伺服器在Set-Cookie以後,傳送給客戶端的
Set-Cookie: id=icyfenix; Expires=Wed, 21 Feb 2020 07:28:00 GMT; Secure; HttpOnly
而客戶端在收到指令以後,後面的請求,都會自動附帶鍵值對資訊
GET /index。html HTTP/2。0Host: icyfenix。cnCookie: id=icyfenix
Cookie-Session 是最傳統的,但在今天依然廣泛應用於大量系統中的、由服務端與客戶端聯動來完成的狀態管理機制。一般來說,系統會把狀態資訊儲存在服務端,而在 Cookie 裡只傳輸一個無字面意義的、不重複的字串,通常習慣上是以 sessionid 或者 jsessionid 為名。然後,伺服器拿這個字串為 Key,在記憶體中開闢一塊空間,以 Key/Entity 的結構,來儲存每一個線上使用者的上下文狀態,再輔以一些超時自動清理之類的管理措施。
這種傳統的方式非常適合與單體服務,也在歷史長河中起著重要的重要,現在我們的所有應用中依然是有著頑強的生命力。
對於叢集的時候,我們在考慮擴充套件的時候,應該如何選擇呢?
因為 Session 儲存在伺服器的記憶體中,那麼當伺服器水平拓展成多節點時,我們在設計時就必須在以下三種方案中選擇其一
要麼就犧牲叢集的一致性(Consistency),讓均衡器採用親和式的負載均衡演算法。
比如根據使用者 IP 或者 Session 來分配節點,每一個特定使用者發出的所有請求,都一直被分配到其中某一個節點來提供服務
,每個節點都不重複地儲存著一部分使用者的狀態,如果這個節點崩潰了,裡面的使用者狀態便完全丟失。
要麼就犧牲叢集的可用性(Availability),
讓各個節點之間採用複製式的 Session,每一個節點中的 Session 變動,都會發送到組播地址的其他伺服器上,這樣即使某個節點崩潰了,也不會中斷某個使用者的服務。
但 Session 之間組播複製的同步代價比較高昂,節點越多時,同步成本就越高。
要麼就犧牲叢集的分割槽容錯性(Partition Tolerance),讓普通的服務節點中不再保留狀態,
將上下文集中放在一個所有服務節點都能訪問到的資料節點中進行儲存。
此時的矛盾是資料節點就成為了單點,一旦資料節點損壞或出現網路分割槽,整個叢集都不能再提供服務。
上面是比較流行的方案,也更加規範,但是現在的服務中,微服務的百家齊放,也導致方案的千差萬別,適合自己的才是最合適,作者現在所在的專案,其實還是基於很早的設計方案,並沒有採用以上的方案,當初的設計中,把一些驗證資訊加密以後存到了cookie裡面,在每次訪問請求的時候,然後解開cookie,驗證其中的資訊,裡面主要是使用者的關鍵資訊,permission,公司唯一ID,過期時間,還有一些使用者的特性資訊,每次都會驗證完以後更新過期時間,透過過期時間來判斷是否需要下線,缺點就是cookie裡面的資訊量太大了,曾經由於新手follow,亂用,造成size太大,影響到了客戶的正常操作,還有就是完全信任cookie,無法保證在cookie在被劫持的情況下的安全問題,其他不多說。
JWT:解決認證授權問題的無狀態方案
JWT跟 Cookie-Session 並不是完全對等的解決方案,它只用來處理認證授權問題,是 Cookie-Session 在認證授權問題上的替代品,充其量能攜帶少量非敏感的資訊,其實這一點跟上面談到的有點類似了,但是JWT在防止中間人篡改做的更好,依舊不能解決被劫持的問題,關於這一點問題,後面也會有專門的文章來分析,
如果你感興趣,請記得關注我
。
我們來看下JWT
JWT令牌是如何發起HTTP請求的
GET /restful/products/1 HTTP/1。1Host: icyfenix。cnConnection: keep-aliveAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。eyJ1c2VyX25hbWUiOiJpY3lmZW5peCIsInNjb3BlIjpbIkFMTCJdLCJleHAiOjE1ODQ5NDg5NDcsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiIsIlJPTEVfQURNSU4iXSwianRpIjoiOWQ3NzU4NmEtM2Y0Zi00Y2JiLTk5MjQtZmUyZjc3ZGZhMzNkIiwiY2xpZW50X2lkIjoiYm9va3N0b3JlX2Zyb250ZW5kIiwidXNlcm5hbWUiOiJpY3lmZW5peCJ9。539WMzbjv63wBtx4ytYYw_Fo1ECG_9vsgAn8bheflL8
JWT是使用 Base64URL 轉碼後直接得到明文, JWT
只解決防篡改的問題,並不解決防洩露的問題
,所以令牌預設是不加密的。
JWT 令牌的三部分結構
1.令牌的第一部分是令牌頭(Header)
{ “alg”: “HS256”, “typ”: “JWT”}
它描述了令牌的型別(統一為 typ:JWT)以及令牌簽名的演算法,示例中 HS256 為 HMAC SHA256 演算法的縮寫,其他各種系統支援的簽名演算法你可以參考JWT 官網。
2.令牌的第二部分是負載(Payload)
JWT 的負載部分是可以完全自定義的,我們可以根據具體要解決的問題,設計自己所需要的資訊,只是總容量不能太大,畢竟它受 HTTP Header 大小的限制,JWT也是有一套自己的標準的,很多欄位已經提前給你預置好了,降低了後期開發的難度。
JWT標準中,推薦了七項宣告名稱
iss(Issuer):簽發人。
exp(Expiration Time):令牌過期時間。
sub(Subject):主題。
aud (Audience):令牌受眾。
nbf (Not Before):令牌生效時間。
iat (Issued At):令牌簽發時間。
jti (JWT ID):令牌編號。
3.令牌的第三部分是簽名(Signature)
使用在物件頭中公開的特定簽名演算法,透過特定的金鑰(Secret,由伺服器進行保密,不能公開)對前面兩部分內容進行加密計算,產生簽名值。
JWT 預設的簽名演算法 HMAC SHA256 是一種帶金鑰的雜湊摘要演算法,加密與驗證過程都只能由中心化的授權服務來提供,所以這種方式一般只適合於授權服務與應用服務處於同一個程序中的單體應用。
JWT 令牌的缺陷
令牌難以主動失效
JWT 令牌一旦簽發,理論上就和認證伺服器沒有什麼瓜葛了,在到期之前就會始終有效,除非我們在伺服器部署額外的邏輯去處理失效問題,而這對某些管理功能的實現是很不利的。
方案
如果我們採用 JWT,就必須設計一個“黑名單”的額外邏輯,把要主動失效的令牌集中儲存起來,而無論這個黑名單是實現在 Session、Redis 還是資料庫當中,都會讓服務退化成有狀態服務,這就降低了 JWT 本身的價值。
只能攜帶相當有限的資料
HTTP 協議並沒有強制約束 Header 的最大長度,但是,各種伺服器、瀏覽器都會有自己的約束,比如 Tomcat 就要求 Header 最大不超過 8KB,而在 Nginx 中則預設為 4KB。所以在令牌中儲存過多的資料,不僅耗費傳輸頻寬,還有額外的出錯風險。
必須考慮令牌在客戶端如何儲存
如果在授權之後,操作完關掉瀏覽器就結束了,那把令牌放到記憶體裡面,壓根不考慮持久化,其實才是最理想的方案。