精华 浅谈cnode社区如何防止csrf攻击
发布于 9 年前 作者 nqdy666 30350 次浏览 最后一次编辑是 8 年前 来自 分享

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

22 回复

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

这样的防御csrf机制,只是防御一些url劫持或xss之后的csrf,加了token 就算你判断referrer 也不可能防刷。 如果写一个自动化的脚本,自动化地执行以下逻辑: 先向种token的url发起一个get请求取得token,再向需要刷的/api接口发送post/get请求,这层防御机制就被破了。 加referrer也是对于来自浏览器的http请求进行了限制, 但是黑客写自动化脚本伪造的请求,你根本不可能用token+referrer的形式防住。 要防,还是得上万恶的验证码。

@zhuyingda csrf是防止跨站请求伪造。 比如你在一台机器同时打开了两个标签页a网站和b网站, 如果用户a网站已经登入,并打开b网站点击了某个按钮,刚好这个按钮背后伪造并发起a网站的请求,比如购买商品的请求,因为cookie对同一个url是共享的,会导致用户被恶意购买商品。

csrf是解决此类问题,并不是为了解决防刷问题。要解决防刷应该在服务端对ip等信息做访问限制,万恶的验证码也可以,不过验证码用户体验真的太差了,登入的时候用用也就可以了。

还设置了不允许外部网站使用iframe引入cnode X-Frame-Options:SAMEORIGIN

rails的默认做法, 贴出了node的实现, 当赞!

@nqdy666 对于什么是csrf,道哥的《白帽子web安全》清楚地说了,“如果一个请求中所有参数都是可以预期的,那么这个请求就有被csrf的风险”,我在博客中也对csrf的各种应用场景和实践经历进行了描述: 浅谈CSRF攻击

如果一定要抱着“跨站请求伪造”这个带有历史原因的漏洞名字不放, 那么XSS的名字还叫跨站脚本攻击呢,你能说你在一个留言板里直接输入个alert(1)那不算xss吗?

也就是说,如果刻意写代码来获取token,然后伪造请求,这种攻击这个方法是不是就防止不了?

@codepandy 我的理解,貌似如果先去获取到token,是不是就防止不了呢

@zhuyingda 我想问下,你说的 再向需要刷的/api接口发送post/get请求,这层防御机制就被破了。这里不用考虑跨域的问题吗? 另外 @nqdy666 上文中如果优先用参数的token 是有问题的,如果伪造一个地址 用iframe 嵌个form表单(例如收藏文章接口,虽然有X-Frame-Options 但只是不能显示 不影响提交)然后随便在网站上拿个token写死,别人访问这个地址,就能收藏这篇文章了。 所以,验证参数上的token是不安全的

回到顶部