csrf是什么 csrf(Cross-site request forgery)跨站请求伪造, 具体谷歌度娘.
防止csrf攻击简单思路: 在服务器上生成一个token, web端发起的请求都带上token这个参数, 请求中的token与服务端的token不一致,则抛出错误.
涉及主要模块代码 layout.html, jquery-ujs.js, app.js, csurf模块
1 服务端如何生产成token, token存储的位置 app.js
app.use(function (req, res, next) {
if (req.path.indexOf('/api') === -1) {
csurf()(req, res, next);
return;
}
next();
});
因csurlf未带入参{ cookie: true }, 故token将会保存session中(其实session不是存放token, 而是能一个secret, 通过secret可生产token且能还原token) csurf()(req, res, next); 该行代码被执行时, 会在为req添加一个方法csrfToken, 接下来来会用到, 如下所示. app.js
app.use(function (req, res, next) {
res.locals.csrf = req.csrfToken ? req.csrfToken() : '';
next();
});
req.csrfToken() ; 该行代码被执行,会生成一个token, 并保存session中. req.csrfToken()保存token的部分代码, 如下所示.
csurf模块 index.js
// generate & set new secret
if (sec === undefined) {
sec = tokens.secretSync()
setsecret(req, res, sec, cookie)
}
2 服务端什么时候校验 app.js
app.use(function (req, res, next) {
if (req.path.indexOf('/api') === -1) {
csurf()(req, res, next);
return;
}
next();
});
除/api开头请求, 都会执行csurf方法, token的校验也是在该方法里, csurf方法部分代码如下所示 csurf模块 index.js
verifytoken(req, tokens, secret, value(req))
3 服务端校验的token, 可能来自哪里. web端请求中的token, 不一定只来自于所传送的参数. csurf模块 index.js
function defaultValue(req) {
return (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token'])
|| (req.headers['x-xsrf-token']);
}
因csurlf初始化时没带任何参数, 故校验的token使用使用默认的值, 从代码上, 默认的token可能来自req.body, req.query, req.header
4 哪些请求不校验token. 从app.js 的代码中可以看出, /api开头的请求是不会校验的, 但果真如此么, 如果真是这样, 那不是访问首页, 都会被拦截. csurf模块 index.js
// verify the incoming token
if (!ignoreMethod[req.method]) {
verifytoken(req, tokens, secret, value(req))
}
从代码上, 部分req.method方法是被忽略校验的, 那到底是什么mehtod呢, 具体如下所示 csurf模块 index.js
// ignored methods
var ignoreMethods = options.ignoreMethods === undefined
? ['GET', 'HEAD', 'OPTIONS']
: options.ignoreMethods
因csurlf初始化时没带任何参数, 故使用默认的ignoreMethods, get head option会被忽略校验, 所以我们就可以很正常的访问首页
5 token什么时候到页面上的. app.js
app.use(function (req, res, next) {
res.locals.csrf = req.csrfToken ? req.csrfToken() : '';
next();
});
layout.xml
<meta content="_csrf" name="csrf-param">
<meta content="<%= csrf %>" name="csrf-token">
在页面初始化的时候, token被放在到了 meta标签后, 之后请求数据, 就可以利用这些值了.
6 web端token参数什么时候传到服务端. web端的token需要时请求时传入的, 但是现在只有meta标签有csrf-token的值, 这个token时什么时候被利用的呢 jquery.ujs.js
// Make sure that every Ajax request sends the CSRF token
CSRFProtection: function (xhr) {
var token = $('meta[name="csrf-token"]').attr('content');
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
},
// making sure that all forms have actual up-to-date token(cached forms contain old one)
refreshCSRFTokens: function () {
var csrfToken = $('meta[name=csrf-token]').attr('content');
var csrfParam = $('meta[name=csrf-param]').attr('content');
$('form input[name="' + csrfParam + '"]').val(csrfToken);
},
jquery.ujs.js
$.ajaxPrefilter(function (options, originalOptions, xhr) {
if (!options.crossDomain) {
rails.CSRFProtection(xhr);
}
});
jquery.ujs.js
$(function () {
rails.refreshCSRFTokens();
});
从代码中看出, 在发ajax请求的时候,会在header.X-CSRF-Token附上token值. 在form表单请求的时候, 会添加token的值.
7 最后 以上代码摘录于cnnode源码git版本号为a511357454c3352a6deb2719ca24eb4250667fff 以上如有理解不当之处, 望提点指正.