2

I am trying to implement csrf protection into my project but I can't make it work with jQuery Ajax. (It works with normal posts requests, though)

If I tamper the token using chrome dev tools before I send the form, I still see "data is being processed" text rather than invalid csrf token error.

app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var csrf = require('csurf');
var bodyParser = require('body-parser');
var router = express.Router();
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
//app.set('strict routing', true);
app.set('view options', {layout: false});

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());

var csrfProtection = csrf({ cookie: true });
var parseForm = bodyParser.urlencoded({ extended: false });

app.use(express.static(path.join(__dirname, 'public')));

app.get('/form', csrfProtection, function(req, res) {
    // pass the csrfToken to the view
    res.render('send', { csrfToken: req.csrfToken() });
});


app.post('/form', parseForm, csrfProtection, function(req, res) {
    res.send('data is being processed');
});


// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

module.exports = app;

send.jade

html
    head
        meta(name='_csrf', content='#{csrfToken}')
    body
        form(action='/form', method='POST')
            |   Favorite color:
            input(type='text', class="favori", name='favoriteColor')
            button(type='submit') Submit
    script(src="javascripts/frontend/jquery/jquery-3.0.0-alpha1.js")
    script(src="javascripts/test.js")

test.js

$(document).ready(function () {

    $.ajaxSetup({
        headers: {'X-CSRF-Token': $('meta[name="_csrf"]').attr('content')}
    });

    $('button').click(function (e) {
        e.preventDefault();
        var text = $('.favori').val();
        alert(text);
        $.post(
            "/form",
            {
                text: text
            }, function (data) {
                console.log(data);
            });
    });
});
6
  • 1
    try to send the csrf token inside the payload message, $.post( "/form", { text: text, _csrf : $('meta[name="_csrf"]').attr('content') }, function (data) { console.log(data); }); Commented Nov 10, 2015 at 17:02
  • It works, but then I need to include csrf for each ajax request. Is there a way to achieve the same thing using ajaxSetup so I won't need to repeat the same thing? Commented Nov 10, 2015 at 17:33
  • Probably no, the last time that I tried to do that, I needed to include the _csrf token inside the payload ajax message. Can I answer your question so you can accept? Commented Nov 10, 2015 at 18:47
  • Maybe you can create a Jquery plugin to automatically get the _csrf token and insert into your payload message. Only an idea... Commented Nov 10, 2015 at 18:48
  • Yes, answer it please. // Good idea, I'll look into that. Commented Nov 10, 2015 at 18:55

3 Answers 3

5

Send the CSRF token inside the payload message:

$('button').click(function (e) {
    e.preventDefault();
    var text = $('.favori').val();
    alert(text);
    $.post(
        "/form",
        {
            text: text,
            _csrf : $('meta[name="_csrf"]').attr('content')
        }, function (data) {
            console.log(data);
        });
});

To facilitate your work I think you can create a Jquery plugin to do it, something like this:

(function( $ ) {
    $.postCSRF = function(to, message, callback) {
        message._csrf = $('meta[name="_csrf"]').attr('content');
        $.post(to, message, callback);
    };
}( jQuery ));

// Usage example:
$.postCSRF('/form',{text:'hi'},function(res) {
    console.log(res);
});

Example: http://jsfiddle.net/w7h4Lkxn/

Sign up to request clarification or add additional context in comments.

3 Comments

Damn, that's great. Thanks.
Sweet. Alternatively you can add the csrf in a hidden input.
How do we validate the token in the server side? Do we need to store it in cookie or something?
0

Check the header to see if its passing the tampered token in the cookie or as part of the form data. It looks like your setup is for using cookies. So changing it on the form shouldn't affect the cookie. Lemme know if that helps reveal the issue.

3 Comments

It sends _csrf token (cookie) with each request, so it's working. But how can I "verify" it when a request is made?
Check out index.js github.com/expressjs/csurf/blob/master/index.js you'll want to implement verify token in the route.
@salep when you look at the csurf index.js you'll notice csrf function has next tick which passes flow back to the route the other functions do notexecute, you'll need to call those function specifically if you want access to their usage. The bottom of index.js has csrf verifytoken.
0

your doing everything exactly right but you have to disable checking for the cookie entirely!

var csrfProtection = csurf({ cookie: false });

the author mentions it here https://github.com/expressjs/csurf/issues/52

thanks for the above code with the header post as this was crucial for validating the express session token and i have shared it with others

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.