23

我正在尝试使用 express.js 框架在使用 node.js 构建的应用程序中实现 CSRF 保护。该应用程序大量使用了对服务器的 Ajax 发布调用。我了解连接框架提供 CSRF 中间件,但我不确定如何在客户端 Ajax 发布请求的范围内实现它。

在 stackoverflow 中发布的其他问题中对此有一些零碎的内容,但我还没有找到一个相当完整的示例来说明如何从客户端和服务器端实现它。

有没有人有一个他们愿意分享如何实现这一点的工作示例?我见过的大多数示例都假设您在服务器端呈现表单,然后将其(连同嵌入式 csrf_token 表单字段)发送到客户端。在我的应用程序中,所有内容都通过 Backbone.js 在客户端(包括模板)呈现。服务器所做的只是提供 JSON 格式的值,这些值被客户端 Backbone.js 中的各种模型使用。据我了解,我需要先通过 ajax 检索 csrf_token,然后才能使用它。但是,我担心从安全角度来看这可能是个问题。这是一个有效的担忧吗?

4

5 回答 5

33

可以通过meta为 CSRF 令牌添加标签然后在每个 Ajax 请求中传递 CSRF 令牌来完成

服务器

添加 CSRF 中间件

app.use(express.csrf());
app.use(function (req, res, next) {
  res.locals.token = req.session._csrf;
  next();
});

您可以通过元标记将 CSRF 令牌传递给客户端。例如,在Jade

meta(name="csrf-token", content="#{token}")

客户

jQuery 有一个称为 ajaxPrefilter 的功能,它允许您提供一个回调,以便在每个 Ajax 请求中调用。然后使用 ajaxPrefilter 设置标题。

var CSRF_HEADER = 'X-CSRF-Token';

var setCSRFToken = function (securityToken) {
  jQuery.ajaxPrefilter(function (options, _, xhr) {
    if (!xhr.crossDomain) {
      xhr.setRequestHeader(CSRF_HEADER, securityToken);
    }
  });
};

setCSRFToken($('meta[name="csrf-token"]').attr('content'));
于 2013-08-04T10:14:26.983 回答
6

服务器.js

...
// All Cookies/Sessions/BodyParser go first
app.use(express.csrf());
...
// Get the request
app.post('/ajax', function(req, res){
    res.render('somelayout', {csrf_token: req.session._csrf});
});

在 somelayout.jade

input(type='hidden', name='_csrf', value=csrf_token)

CSRF 中间件在每个会话中只生成一次 csrf 令牌,因此在用户访问期间它可能不会改变。

此外,它不会检查 GET 和 HEAD 请求上的令牌。只要令牌在请求中(标头、正文或查询),就可以了。这就是它的全部内容。

于 2012-06-26T03:03:21.040 回答
3

由于您正在使用Backbone.js您的应用程序,我假设它是一个 SPA 并且您最初加载一个index.html文件,然后通过ajax调用发出任何其他请求。如果是这样,您可以将一小段 JS 代码添加到您的index.html文件中,以保存 crsf 令牌以供将来ajax调用。

例如:

index.html(使用 Handlebars 进行模板...)

<!DOCTYPE html>
<html>
    <head>
        ...
        <script type="text/javascript">
            $( function() {
                window.Backbone.csrf = "{{csrfToken}}";
            });
        </script>
    </head>
    <body>
        ...
    </body>
</html>

渲染index.html文件时,csrf将 express 框架在此处生成的令牌提供给它:req.session._csrf

使用时Backbone.js,它会设置一个名为 的全局变量Backbone。前一个函数所做的只是设置一个调用csrf全局Backbone对象的属性。当您ajax调用POST数据时,只需将Backbone.csrf变量添加到_csrf通过ajax调用发送的数据中即可。

于 2013-03-15T20:21:05.647 回答
1

在服务器中:

app.use(function (req, res) {
  res.locals._csrf = req.csrfToken();
  res.locals.csrf_form_html = '<input type="hidden" name="_csrf" value="' + req.csrfToken() + '" >';
  req.next();
});

在客户端:(swig 模板)

var csrf = {{ _csrf|json|safe }};

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': csrf
  }
});

$.post("/create", data, function(result) {
  console.log(result);
}).fail(function(){
  console.log(arguments);
});
于 2014-03-23T16:00:47.287 回答
0

1.添加csrf保护中间件:

app.use(csrf({cookie: true}));

// csrf middleware
app.use(function (req, res, next) {
   res.cookie('X-CSRF-Token', req.csrfToken());
   // this line below is for using csrfToken value in normal forms (as a hidden input)
   res.locals.csrfToken = req.csrfToken(); 
   next();
});

// routing setup goes here

2.使用 :添加beforeSend回调$.ajaxSetup(在所有 ajax 调用之前添加)

$.ajaxSetup({
beforeSend: function (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;
    }

    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
        // Only send the token to relative URLs i.e. locally.
        xhr.setRequestHeader("X-CSRF-Token", getCookie('X-CSRF-Token'));
    }
}
});

3.就是这样!现在您可以发送 ajax 请求,并且不需要在标头中添加任何内容或作为请求参数来传递 csrf。

于 2017-07-06T09:37:12.743 回答