精华 浅谈cnode社区如何防止csrf攻击
发布于 14小时前 作者 nqdy666 156 次浏览 来自 分享

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 以上如有理解不当之处, 望提点指正.

5 回复

哎哟,被加精了,感谢感谢~.~

回到顶部