I am trying to implement CSRF protection in an app built using node.js using the express.js framework. The app makes abundant use of Ajax post calls to the server. I understand that the connect framework provides CSRF middleware, but I am not sure how to implement it in the scope of client-side Ajax post requests.
There are bits and pieces about this in other Questions posted here in stackoverflow, but I have yet to find a reasonably complete example of how to implement it from both the client and server sides.
Does anyone have a working example they care to share on how to implement this? Most of the examples I have seen, assume you are rendering the form on the server-side and then sending it (along with the embedded csrf_token form field) to the client-side. In my app, all content is rendered on the client-side (including templates) via Backbone.js. All the server does is provide values in JSON format, which are utilized by various Models in Backbone.js on the client-side. By my understanding I would need to retrieve the csrf_token via ajax first before it can be used. However, I am concerned this may be problematic from a security standpoint. Is this a valid concern?
When accessing protected routes via ajax both the csrf token will need to be passed in the request. Typically this is done using a request header, as adding a request header can typically be done at a central location easily without payload modification. The CSRF token is obtained from the req.csrfToken () call on the server-side.
req.query._csrf - a built-in from Express.js to read from the URL query string. req.headers ['csrf-token'] - the CSRF-Token HTTP request header. req.headers ['xsrf-token'] - the XSRF-Token HTTP request header. req.headers ['x-csrf-token'] - the X-CSRF-Token HTTP request header.
CSRF Attack Request. To validate the authenticity of the delete request, the user's browser stores the session token as a cookie. However, this leaves a CSRF vulnerability in your application. An attacker can send a delete request to your server with the cookie present in the browser.
But according to the Express docs on csrf: The default value function checks req.body generated by the bodyParser () middleware, req.query generated by query (), and the "X-CSRF-Token" header field. So depending on your client side framework, you could also use the query string or the X-CSRF-Token alternatives.
It can be done by adding meta
tag for CSRF token and then pass CSRF token with every Ajax request
Add CSRF middleware
app.use(express.csrf());
app.use(function (req, res, next) {
res.locals.token = req.session._csrf;
next();
});
You can pass a CSRF token to the client side via, say, a meta tag. For ex, in Jade
meta(name="csrf-token", content="#{token}")
jQuery has a feature called ajaxPrefilter, which lets you provide a callback to be invoked every Ajax request. Then set a header using 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});
});
In somelayout.jade
input(type='hidden', name='_csrf', value=csrf_token)
The CSRF middleware only generates the csrf token once per session, so it will probably not change for the duration of a user's visit.
Also, it doesn't check for the token on GET and HEAD requests. As long as the token is in the request (header, body, or query), you're good. That's pretty much all there is to it.
Since you are using Backbone.js
for your application, I am assuming that it is a SPA and you initially load an index.html
file, then make any other requests are made via ajax
calls. If so, you can add a small snippet of JS code to your index.html
file to hold the crsf token for any future ajax
calls.
For example:
index.html (using Handlebars for templating...)
<!DOCTYPE html>
<html>
<head>
...
<script type="text/javascript">
$( function() {
window.Backbone.csrf = "{{csrfToken}}";
});
</script>
</head>
<body>
...
</body>
</html>
When you render the index.html
file, give it the csrf
token that the express framework generated here: req.session._csrf
When you use Backbone.js
, it sets a global variable called Backbone
. All that the previous function is doing is seting a property called csrf
to the global Backbone
object. And when you make an ajax
call to POST
data, simply add the Backbone.csrf
variable to the data as _csrf
that is being sent via the ajax
call.
In Server:
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();
});
In Client: (swig template)
var csrf = {{ _csrf|json|safe }};
$.ajaxSetup({
headers: {
'X-CSRF-Token': csrf
}
});
$.post("/create", data, function(result) {
console.log(result);
}).fail(function(){
console.log(arguments);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With