本篇速覽腦圖
名詞約定
全局會話
在SSO登入頁面登入後,我們就認為建立起了全局會話
判定标志
SSO頁面的session存在且未過期
局部會話
在各個子系統,是否已經登入過,這個我們稱為局部會話
判定标志
子系統存在可行的token【未過期且有效】
ticket
SSO系統頒發給子系統的憑證,有此憑證且有效的話,表明SSO系統允許子系統去建立局部會話【生成token】
token
子系統的通路憑證,各個子系統的token是不一樣的,具體視業務而定,子系統也需要配置相應的攔截器來檢測token
SSO登入
當SSO登入頁面登入成功後,會存儲一份session,建立起會話,表示全局會話已存在 session我們這裡就不再過多贅述,想了解更多的話可以參考本專欄往期内容
使用者通路流程
直接通路子系統A分成了兩種情況:SSO已登入或未登入【全局會話是否存在】
我們先列出時序圖,後續分析起來對照時序圖會更清晰一點
ps:時序圖裡邊涉及到了一些代碼細節,不清晰的地方可以先跳過,後續講解代碼的時候再着重解讀
SSO已登入
注意:這裡我們分析的是SSO已登入,也就是全局會話已存在的情況
- melo直接通路子系統A,此時會優先判斷局部會話是否存在且有效【token】
- 若有效則直接放行,沒有SSO什麼事情了,業務正常執行
- 若無效,說明局部會話不存在,此時去判斷全局會話
- 接下來會跳轉到SSO頁面,SSO頁面調用SSO服務接口,判斷全局會話是否存在,發現session存在且有效
現在全局會話校驗成功了,接下來的問題就是如何建立局部會話?
- 局部會話依賴于全局會話,是以需要全局會話去頒發ticket給子系統A【相當于一個授權的過程,允許子系統去登入】
- 子系統拿到ticket後,校驗是否是SSO頒發的且有效,有效則解析出ticket裡邊的憑證【比如學号】,然後根據學号在子系統A生成局部會話token,至此局部會話也建立成功了。
SSO未登入
SSO未登入的流程,跟上邊大體是相同的,隻不過在SSO頁面判斷全局會話不存在時【session為空】,此時需要跳轉到SSO登入頁面,登入成功生成session後 ,再去給子系統頒發ticket
細節
SSO如何跳轉回子系統A
子系統A跳轉到SSO的時候,需要傳遞參數redirect_url,後續根據這個url就能跳轉回去
ticket如何傳遞給子系統A
一般是從SSO跳轉到子系統A的時候,拼接在位址欄後邊,比如melo.com?ticket=xxx
不同系統需要共用redis嗎?
不需要的,我們用遠端調用的方式,去調用各個系統的接口,各個系統接口内部,就能通路各自主機redis,而不需跨系統去通路其他主機的redis
安全優化
ticket如何防範被篡改?
ticket裡邊是有使用者憑證的,黑客如果篡改了ticket裡邊的使用者憑證,比如改成黑客自己的,那到子系統A登入的時候,登入的就是黑客的身份了。
此處melo的解決方法,其實是類似jwt,頒發ticket的時候,是用jwt的加密方式【結合數字簽名】
此處不清晰的同學,可以參考本專欄往期内容
隻要加密數字簽名的私鑰不洩露,黑客就沒辦法自行篡改憑證後,捏造出對應的數字簽名。
ticket如何防盜用?
ticket拼接在位址欄,安全風險還是蠻高的,如果黑客拿到我們的ticket,豈不是能直接去子系統A登入了?
此處melo的解決方法是:SSO頒發ticket的時候,擷取此時使用者的ip,采用JWT機制,并在payload裡邊綁定使用者的ip,到子系統A的時候,校驗ticket有效性的時候,再次擷取使用者ip,并解析出payload裡邊的ip,對照兩個ip是否一緻,一緻說明是同一個使用者。
此方法的缺點是:黑客能夠看到payload裡邊的ip,如果黑客僞造自己ip為payload裡邊的,這樣就能通過我們的校驗了。
ticket如何設定隻允許使用一次?
ticket如果可以被多次使用,也會帶來一定的風險。
此處melo的解決方法是:SSO頒發ticket的時候,就把ticket存儲在redis中,并設定1min過期
子系統A驗證ticket有效性的時候,遠端調用SSO的verify接口【接口的功能是:判斷SSO的redis中ticket是否已過期】,未過期則說明有效,并删除掉該ticket
- 若已過期,則說明已失效了,需要重新頒發
- 細心的同學也許會發現,遠端調用其實就解決了,不同系統需要共用redis的問題,如果是直接在子系統A去驗證的話,需要去通路SSO的redis【但由于防火牆問題,redis一般都是設定僅本機可通路的】
- 而如果用遠端調用的話,是去調用sso的verify接口,此接口隻需要判斷本機上的redis是否存在ticket即可
防重放
比如我們有這樣一個接口,查詢支付訂單的接口,調用該接口時出于安全性,需要綁定一個簽名,簽名由訂單的場地id+時間段等資訊,末尾再加上通訊雙方約定好的私鑰 secret ,用md5加密生成sign,作為接口的簽名傳遞到服務端去校驗
服務端用同樣的資訊和規則,結合 secret ,用md5加密後,判斷兩個簽名是否一緻
雖然這樣解決了接口資訊篡改的問題,黑客無法自行僞造簽名來發請求,但如果黑客抓取到我們的一個合法請求後,其實是可以一直用這個合法請求,去瘋狂調用我們接口的~
什麼是重播攻擊
是以,重播攻擊就是:黑客攔截了我們的請求,獲得我們發給服務端的一個合法請求,然後重複地發送該合法請求,若該請求耗時過長,極端調用可能會搞崩我們的系統
注意 -- 跟幂等的差別
幂等是允許執行多次,隻不過執行多次後的結果依然是一樣的 而防重放,是不允許執行多次,從根頭上就遏制住了
如何防止重播攻擊?
原理其實很簡單,就是讓合法的請求,隻能被執行一次,保證每次請求的唯一性
時間戳 timestamp
我們認為一次HTTP請求從發出到到達伺服器的時間是不會超過60s的,當你發送一個請求時必須攜帶一個時間戳timestamp,假設值為20 。
當請求發送到伺服器之後,伺服器會取出伺服器的目前時間, 假設為 now =100, 很明顯 now -timestamp>60s,那麼伺服器就認為請求是不合法的。
防止時間戳被更改
一般情況下,黑客從抓包重放請求耗時遠遠超過了60s,但如果黑客修改了時間戳為目前時間,使得 now-timestamp < 60s 的話,似乎就能繞過我們的校驗了诶?
是以此時我們會結合上文的 防篡改 機制 , 具體流程如下:
- 用戶端對 傳輸的參數資訊【時間戳等】+約定好的secret 進行 md5 加密,得到sign,一并傳遞給服務端
- 服務端重複該過程,對 收到的參數資訊【時間戳等】+約定好的secret 進行 md5 加密,得到sign
- 判斷是否跟用戶端發送過來的sign一緻,若一緻則說明接口參數資訊沒有被篡改過
存在問題
如果黑客在60s再次發起請求的話,那還是防範不了的
随機數nonce
nonce的意思是僅一次有效的随機字元串,是以需要做到每次請求的 nonce 都不同,可以由時間戳來生成,結合 ip 位址等,進一步確定唯一性。
用戶端請求接口的時候,生成随機數,發送到服務端,服務端吧 nonce 存儲在redis裡邊,當重放請求到達時,驗證發現 nonce 已經存在,則認為請求是非法的
存在問題
要保證曆史全局唯一,有點麻煩,并且redis存儲那麼多,開銷有點大
時間戳+随機數
綜合來看,時間戳的問題是沒法防止60s内的攻擊,而随機數的問題在于要做到全局唯一,而且要存儲很多 nonce ,耗費空間大
是以我們其實可以結合兩者,具體流程如下:
- 用戶端生成時間戳和随機數,并且作為sign的加密參數,傳遞給服務端
- 服務端先判斷時間戳是否合法,若不合法,則直接傳回【省去存儲随機數的開銷】
- 若時間戳合法,再将 nonce 存儲到redis裡邊,并且設定 1min過期
注意這裡設定 1min過期 的好處在于,隻需要保證在這 1min内 ,nonce不會重複即可,這樣用戶端生成nonce也不用太過複雜
後續那些在60s内的攻擊,雖然能繞過時間戳校驗,但卻因為 nonce 的存在【1min過期,是以能防1min内的,且占用記憶體不會過多】,會被認為是非法的。
而60s以外的攻擊,在時間戳校驗就直接GG了。
- 是以此方案,綜合解決了兩者的缺點:無法防止60s的攻擊,以及存儲開銷大
原文連結:https://juejin.cn/post/7160224863964102669