通用安全的token化解決方案
一、協議過程
a. 我們先來看token化的協議過程
圖 一.a
在用戶輸入正確的用戶名和密碼後,"圖 一.a" 中 Step "2." 將tokens放入到cookie中, 拿點融舉例子,我們會將cookie放到 ".dianrong.com」這個域名下。看起來非常簡單,認證、單點登陸全都有了。
b. 我們再來看一看CAS的SSO協議過程
圖 一.b
圖 一.b 描述了開源應用CAS定義的單點登陸協議
有重定向、有指定service, 臨時ticket傳遞、有後台有狀態ticket的驗證,整個流程步驟比較多。倘若我們的應用是單頁的前後端分離應用,前端和後端僅通過ajax進行訪問,在重定向這一步會卡住。
點融的應用幾乎都是前後端分離,我們針對這種情況做了一些定製(本文重點不是這個, 有興趣可以找作者)。
通過這個協議過程的對比,我們發現,token無疑提供了更高的便捷性。
二. Token化的優點和缺點
優點:
? 無狀態、性能——token機制在伺服器端不需要存儲session信息,因為token自身可包含登陸用戶的信息,例如 手機號、用戶id,在客戶端cookie存儲這個信息從性能上來講, 節省一次網路,僅做一次token的驗簽,將io消耗轉換為cpu消耗.
? 解耦—— token在內網生成,可由任意一個集成了SDK的伺服器端 設置到客戶端,它的生成 和 驗證方案可隨意切換,中心伺服器用於維護狀態
? 適用於現代Clients—— 比如微信小程序、比如原生平台
? 便捷 ——集成方便、測試方便、客戶端透明
? 標準 ——比如RFC7519,使用JWT格式,可以跨平台
ps: token化之後,token理論上可以交給前端,由前端自己決定保存方式、傳遞方式,也因此可以實現跨域變得更加方便方式。 但由此而引出的安全問題後端無法掌控,因而我不認為跨域訪問是一個優勢, 跨域的授權, 應該由oauth2.0協議來完成。
缺點:
? Security—— 便捷 和 安全是一對好夥伴,在獲得便捷的同時應當考慮安全性是否下降
? Revoke token—— token無狀態,屬於Bearer Token 因此無法對已經簽發的token進行撤銷, 只能等待其失效.
? Renew token—— token無狀態,所以不會像session隨著每一次的訪問而修改session失效時間.
三、平衡缺點,獲得優點
A. Security
為了儘可能的限制安全問題發生的範圍,我們生成的所有token都會放入cookie並且cookie.setMaxAge(-1), 常見的security問題以及其解決方案:
1. Man-in-the-Middle.使用https加密,避免中途有人監聽竊取cookie, 以java代碼為例, cookie new出來之後需要 setSecure=true, 只在https的情況下設置cookie.
2. Cross-Site Scripting (XSS), cookie new出來之後需要 setHttpOnly=true, 這樣js代碼就無法操作cookie, 自然XSS無處遁尋。針對伺服器端 和 非WEB客戶端,則需要分別保證自己依賴的library不包含可執行代碼,客戶端保證伺服器返回的任何內容都有執行類似escapeHtml的操作.
3. Cross-Site Request Forgery (CSRF), 在訪問某些比較敏感介面時(比如轉賬),使用Double-Submit Cookie方案解決。 這個安全問題有一點需要注意:不要濫用CORS(cross origin resource share), 這樣會導致 CSRF方式的漏洞, 完全暴露給惡意攻擊者。
B. Renew & Revoke token
想要克服無狀態的這兩個缺點,首先看 兩個概念 : Access & refresh token簽名tokens
? Access token擁有比較短的生存時間, 可以被認作為一個無狀態的可信任的字元串。
? Refresh token擁有比較長的生存時間,是用來換取access token的。refresh token應該可以被撤銷(Database + cache).
基於這兩個從oauth2借來的概念,我們的方案如圖:
圖 三.a server-side use case
圖 三.b client-side 流程圖
如圖三.b, 我們將refresh token有狀態化,通過對refresh token的控制,從而達成對token renew&revoke的目的。
通過Access Token & Refresh token pair 的有效期的配置,我們即擁有了token無狀態的優勢(由access token提供),也擁有了控制token狀態的能力。
以下舉一些常見的token可配置的例子:
? 敏感應用(如 銀行app)
Access token TTL(Time to live) : 1 minute
Refresh token TTL : 30 minutes
? 普通應用
Access token TTL: 5 minutes
Refresh token TTL : 2 hours
? 特殊身份不敏感應用(如今日頭條、抖音)
Access token TTL = 1 hour
Refresh token TTL = 2 years
Balance是程序員世界很有趣的話題。
四、簽名 & 驗簽 演算法
RSA or HMAC or others?
主要從兩個維度考慮問題:
1. 安全
2. 性能
RSA 演算法的安全基於大數因式分解,而大數因式分解無特效公式,因此RSA只要密鑰/公鑰對足夠大,就足夠安全。我在我本機做了一些性能測試, 將簽名內容SHA256之後用RSA演算法簽名驗簽時間如下:
* 4096位RSA私鑰公鑰對 簽名 --> 每個49.36ms, 驗簽 --> 每個 0.6667ms
* 2048位RSA私鑰公鑰對 簽名 --> 每個6.43ms, 驗簽 --> 每個 0.1844ms
* 1024位RSA私鑰公鑰對 簽名 --> 每個1.088ms, 驗簽 --> 每個 0.0548ms
RSA 私鑰和公鑰不一樣,在token做authentication的case中,簽名使用私鑰,驗簽使用公鑰。
在內網,可以僅將公鑰交給非簽發籤名的系統來驗簽(防止其他系統泄露私鑰), 而私鑰僅由簽發籤名的系統保管。
HMAC 只有密鑰,簽名驗簽很快, 客戶端知道密鑰,易泄露。
不論用哪種演算法,都不需要將公鑰或私鑰交給前端。
五、總結
? Token放進cookie, Cookie 應該 setSecure = true , httpOnly, .domain.com, setMaxAge(-1)
? Access Token + Refresh Token的方式 是一個非常好的scaling 策略
? 每一次使用refresh token(如 獲取新access token、refreshtoken) 都需要訪問伺服器詢問其狀態
六.其他風險
? 簽名密鑰泄露
? 密鑰被破解
簽名密鑰泄露之後,理論上得到密鑰的人可製造任意被伺服器相信的內容。需要系統擁有隨時更換密鑰的能力、周期性的更換密鑰。
七、Reference Links:
? 流程圖地址:
https://www.processon.com/view/link/5ae9249de4b09b1bf6369cba
? 滴滴passport經驗:
http://www.hello-code.com/blog/architecture/201607/6099.html
? 講真別再用JWT了!
https://www.jianshu.com/p/af8360b83a9f
? OWASP:
https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_Sheet_for_Java#Token_weak_secret
? Building Secure User interfaces With JWTs(JSON Web Tokens) :
https://www.slideshare.net/stormpath/building-secure-user-interfaces-with-jwts
![](https://pic.pimg.tw/zzuyanan/1488615166-1259157397.png)
![](https://pic.pimg.tw/zzuyanan/1482887990-2595557020.jpg)
※代理協議Proxy protocol
※令人窒息的擼貓操作之崽崽篇
TAG:點融黑幫 |