CSRF令牌使用

5
我对使用生成的csrf令牌来保护我的Web应用程序很感兴趣。我的问题是,我需要如何将该令牌发送回服务器:使用查询参数还是HTTP标头x-csrf-token?两者有何区别?
2个回答

9
由于您正在使用Express,因此可以使用其CSRF中间件(由Connect提供):http://www.senchalabs.org/connect/csrf.html
您可以在此处查看已注释的源代码:https://github.com/senchalabs/connect/blob/master/lib/middleware/csrf.js
您需要做的就是包含该中间件,然后在您的POST表单(或PUT等任何更改状态的请求)中将变量_csrf设置为值req.session._csrf。
请参阅此处的示例:https://github.com/senchalabs/connect/blob/master/examples/csrf.js
更新:
自Connect 2.9.0以来,您必须使用req.csrfToken()而不是req.session._csrf
完整示例:https://github.com/senchalabs/connect/blob/master/examples/csrf.js
提交:https://github.com/senchalabs/connect/commit/70973b24eb1abe13b2da4f45c1edbb78c611d250
更新2:
Connect中间件被分成不同的模块(和相关存储库),您可以在此处找到它们(包括CSRF):https://github.com/senchalabs/connect#middleware

是的,我知道这个,但我想知道使用什么更好:一个标头 x-csrf-token 还是隐藏字段? - Erik
正如您在此处所见:https://github.com/senchalabs/connect/blob/master/lib/middleware/csrf.js#L69 Express 首先检查 POST 值,然后是查询字符串值,最后是 x-csrf-token 标头,因此最好传递一个带有值的隐藏 _csrf 字段。 - alessioalex
我想在ajax请求中使用csrf-token,那么我应该采用什么方法? - Erik
一样的,这与普通请求在 CSRF 保护方面没有区别。只需在发送请求时将相同的令牌作为隐藏字段使用即可。 - alessioalex
@alessioalex 嘿,这个 完整示例:https://github.com/senchalabs/connect/blob/master/examples/csrf.js 出现了404错误。 - slevin

3
在我看来,当提交表单时应该在隐藏字段中使用csrf POST参数。这是唯一可行的方式。
但对于AJAX请求,我强烈建议您使用X-CSRF-Token头部。主要是因为如果操作正确,这样做将节省您记住为每个POST请求添加令牌的麻烦。或者,使用jQuery Form等库时,在提交时添加额外的POST参数可能会变得很繁琐。
例如,如果您使用jQuery进行AJAX请求,它会为您提供钩子,您可以在请求发送前自动透明地设置X-CSRF-Token。因此,几乎不需要修改客户端代码。这将使您的代码更加优秀。
基于Django的示例实现,我在几乎所有项目中都成功使用了这种方式。
jQuery(document).ajaxSend(function(event, xhr, settings) {

  function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
        var cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function sameOrigin(url) {
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
           (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
           // or any other URL that isn't scheme relative or absolute i.e relative.
           !(/^(\/\/|http:|https:).*/.test(url));
  }

  function safeMethod(method) {
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
    xhr.setRequestHeader("X-CSRFToken", getCookie('csrf.token'));
  }
});

在服务器端,你只需要设置一个包含CSRF令牌的cookie,这样客户端就可以轻松获取令牌。我用下面的代替了app.use(express.csrf()):
app.use((function(options) {

  var csrf = express.csrf(options);

  return function(req, res, next) {

    function onCsrfCalled() {
      var token = req.session._csrf;
      var cookie = req.cookies['csrf.token'];

      // Define a cookie if not present
      if(token && cookie !== token) {
        res.cookie('csrf.token', token);
      }

      // Define vary header
      res.header('Vary', 'Cookie');

      next();
    }

    csrf(req, res, onCsrfCalled);
  }
})());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接