上一篇中,讲了下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