上一篇中,講了下cookie+session的方式check使用者狀态,但是處理CSRF(跨站請求僞造)上會麻煩一點。
既然說到了CSRF,那先稍微解釋一下。
CSRF
csrf是一種攻擊方法,通俗的就是說攻擊者僞裝成你進行行騙。 這種是如何做到的呢?
從原理上說,這種攻擊方式不需要竊取到使用者的cookie,而是直接使用使用者的cookie去進行違法行為。
怎麼做到的?
我模拟一下這種情況:
有一個網站A,A中有個post或get請求,用于對視訊進行點贊。用戶端和服務端的認證是用cookie進行的(參照第一篇的cookie+session的方式)
現在有個網站B,是攻擊者設定的,可能是通過誘惑标題引你進去,抑或是通過通過xss方式往C網站植入了一個iframe,等等,無論哪種設定,都是當你進入相應的B或C頁面時,就會自動去請求A網站那個點贊的接口。
如果我們之前登陸過網站A,并且并沒有登出,那麼請求A網站那個接口時,浏覽器就會用登陸A網站時建立的cookie,去請求(這是浏覽器自身特性)。比如B網站寫了一個
這時,可就等于是你在點贊了。
這可能會造成一個很大的問題,比如,會讓某個視訊資源點贊數非常高而順序排的非常靠前, 或者是将一個資源标記為舉報,導緻資源下架。
還可能會因為網站的不嚴謹造成更嚴重的資金損失的問題,等等等等。
到這裡,大家可能會看到問題的嚴重性了。前端同學因為之前不需要關注這些問題,當轉到node時,也很容易忽略這類安全問題。
如何防範
如果大家通過上面的例子知道了深層原因,那也就有了大概的防範思路。但是我們作為技術開發者,不能因為有這種問題而強制使用者每次通路後都立即登出,這對使用者是非常不友好的。
對了,那我們就不讓居心不良的人使用cookie通過session讓服務端确定使用者。也就是說,讓其即使在B網站進行A網站的接口請求,A網站服務端并比對不上使用者,導緻登入不成功,那就沒有問題了。
是以,token這種形式就特别适合了。
token
1. token 是什麼
可以從網上搜一下,會看到很多的解釋。我想通俗的說一下,就是在登入成功時,我們産生一段唯一的,不會輕易被解析的字元串,發送到用戶端,用戶端把這個字元串存起來,每次請求時把這段字元串帶着,讓服務端反解。
看到這裡,是不是感覺跟cookie+session的形式差不多? 是的,從原理上來說,是差不多的,隻是token我們一般不把它放在cookie中,會放在比如loaclstorage中,即便壞人想用csrf的方式搞破壞,但是,他拿不到token,是以也就沒法讓服務端認證為登入狀态,他的陰謀也就無法得逞了。
2. 如何做?
第一,在node中生成好token
// 寫一個中間件token-middleware.js
const setting = require('../../config/setting');
const verify = require('../../config/verify');
function tokenMiddleWare(req, res, next) {
let token = req.headers[setting.token.header];
if(token === undefined){
return next();
}else{
// 可以token校驗并将校驗結果儲存至請求頭中
verify.getToken(token).then(data => {
logger.info('校驗的data是:::', data);
req.data = data;
return next();
}).catch(err =>{
logger.error('校驗出現錯誤:', err);
return next();
})
}
}
module.exports = tokenMiddleWare;
複制代碼//setting.js
module.exports = {
token: {
// token密鑰
signKey: '[email protected]@',
// 過期時間300s
signTime: 300,
// 請求頭參數
header: 'authorization',
// 不用校驗的路由
unRoute: [
{url: /\.(jpg|png|css|js)$/, methods: ['GET']}
]
}
}
複制代碼// verify.js
const jwt = require('jsonwebtoken');
const setting = require('./setting');
const verify = {
// 設定token
setToken(username, _id){
return new Promise(resolve => {
let token = jwt.sign(
// 存儲資料,自定義
{username, _id},
// 密鑰
setting.token.signKey,
{expiresIn: setting.token.signTime, algorithm: 'HS256'}
);
resolve(token);
})
},
getToken(token){
return new Promise((resolve, reject) => {
// 處理token字元串
if(!token.split(' ').length){
reject({error: 'The token value is empty'})
}else{
// 解密token并傳回資料
let data = jwt.verify(token.split(' ')[1],setting.token.signKey)
resolve(data)
}
})
}
}
module.exports = verify;
複制代碼
當登入成功時,進行token的設定:
const verify = require('../../config/verify');
// loginInfo.username -> 登入名
// loginInfo.passwd -> 登入密碼
verify.setToken(loginInfo.username, loginInfo.passwd).then(token => {
// 生成token後,傳回給用戶端
res.json({
code: 0,
mesg: 'success',
token
});
});
複制代碼
然後,需要把寫的中間件和express-jwt應用在app.js(你的根檔案)中
const expressJwt = require('express-jwt');
// 加載token中間件
app.use(tokenMiddleware);
// 驗證token是否可用
app.use(expressJwt({
secret: setting.token.signKey,
algorithms: ['HS256'],
credentialsRequired: false, // 允許無token請求
requestProperty: 'auth' // 把解析的值放在req.auth上
})
.unless({
//除了這個path,其他的URL都需要驗證
path: setting.token.unRoute
}));
複制代碼
注意,當使用express-jwt中間件時,需要一個兜底的中間件,來承接解析錯誤、token過期等結果。
如果出現錯誤,我們預設傳回401,是以我們來設定一下。
app.use(function (err, req, res, next) {
// 當驗證token出現問題時,比如對不上,過期等情況,則傳回401
if (err.name === 'UnauthorizedError') {
res.status(401).send(err.message);
}
});
複制代碼
這樣node這一層就處理好了。
注意: 上面自己寫的那個中間件是自己簡單寫的express-jwt功能,是以用express-jwt, 可以不用我寫的那個中間件。
第二,用戶端處理(使用vue)
用戶端,首先要做的,就是儲存服務端傳回的token,我把它儲存到了loaclstorage上。
比如:
res.data.token && localStorage.setItem('authToken', res.data.token);
複制代碼
然後,需要處理每次前端向服務端的請求頭:
// 同樣,還是用axios
// 攔截前端要發出去的請求
axios.interceptors.request.use(config => {
let token = localStorage.getItem('authToken');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
},
error => {
console.error('攔截request出現錯誤', error);
});
複制代碼
這樣,每次前端的請求,都會帶着這個Authorization頭,node層拿到并且解析就可以了。
同時需要注意,如果token解析後,傳回401,那麼我們也需要承接,并且轉到登入頁面。
axios.interceptors.response.use(response => {
return response
},
error => {
if(error.response.status === 401) {
router.push('/login');
}
}
);
複制代碼
token需要注意的問題
要想token不被csrf利用,前提是别讓攻擊者通過xss擷取到,是以,需要處理好xss攻擊。
總結
跟cookie+session的基本原理很相近,都是處理http協定無狀态的情況
token通過設定header頭的形式,避開cookie,防止登入相關的cookie被利用
需要注意處理xss,xss是另外一個攻擊方式,但如果被xss了,token也就有危險了
token跟cookie并不是誰替代誰的問題,而是在什麼場景下用什麼更為合适一些。
好了,關于登入的時候涉及到的點和要規避的坑就先寫到這。
希望大家能有所收獲,用1個多小時的看文章和實地開發測試,解決新手可能要3天才能研究透的問題。 如果大家喜歡,别忘了點個贊哈, 哈哈哈
關于找一找教程網
本站文章僅代表作者觀點,不代表本站立場,所有文章非營利性免費分享。
本站提供了軟體程式設計、網站開發技術、伺服器運維、人工智能等等IT技術文章,希望廣大程式員努力學習,讓我們用科技改變世界。
[從單頁應用看node的token(二)]http://www.zyiz.net/tech/detail-143833.html