如何使用express.js在Ajax调用中实现CSRF保护(查找完整示例)?

我正在尝试使用express.js框架在使用node.js构build的应用程序中实现CSRF保护。 该应用程序充分利用Ajax post调用服务器。 我明白,连接框架提供了CSRF中间件,但我不知道如何在客户端Ajax post请求的范围内实现它。

关于这个在这里在其他问题发布在stackoverflow,但我还没有find一个相当完整的例子,如何从客户端和服务器端实现它。

有没有人有一个他们喜欢分享如何实施这个工作的例子? 我所见过的大多数例子都假设你在服务器端渲染表单,然后将它(连同embedded的csrf_token表单字段)一起发送到客户端。 在我的应用程序中,所有内容都通过Backbone.js呈现在客户端(包括模板)上。 所有服务器都提供JSON格式的值,这些值在客户端的Backbone.js中被各种模型使用。 根据我的理解,我需要先通过ajax检索csrf_token,然后才能使用它。 但是,我担心这从安全的angular度来看可能是有问题的。 这是一个有效的关注吗?

可以通过为CSRF令牌添加meta标记,然后通过每个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的特性,它可以让你提供一个callback,以便在每个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')); 

server.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请求上的令牌。 只要标记在请求(标题,正文或查询)中,你就是好的。 这几乎是所有这一切。

由于您正在为您的应用程序使用Backbone.js ,因此我假设它是一个SPA,并且您最初加载了一个index.html文件,然后通过ajax调用进行其他任何请求。 如果是这样的话,你可以在你的index.html文件中添加一小段JS代码,以保存未来ajax调用的crsf标记。

例如:

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

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

当您渲染index.html文件时,请为其指定csrf表示框架生成的csrf标记: req.session._csrf

当你使用Backbone.js ,它会设置一个名为Backbone的全局variables。 前面的函数所做的就是将一个名为csrf的属性csrf为全局Backbone对象。 而且,当您对POST数据进行ajax调用时,只需将Backbone.csrfvariables作为正在通过ajax调用发送的_csrf添加到数据中_csrf

在服务器中:

 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); }); 

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.使用$.ajaxSetup添加一个$.ajaxSetupcallback$.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 ie locally. xhr.setRequestHeader("X-CSRF-Token", getCookie('X-CSRF-Token')); } } }); 

就这样! 现在你可以发送ajax请求了,你不需要在头文件中添加任何东西,或者作为一个请求参数来通过csrf。