Skip to content
This repository has been archived by the owner on Sep 14, 2022. It is now read-only.

csrf always fails #52

Closed
maplesap opened this issue Feb 11, 2015 · 11 comments
Closed

csrf always fails #52

maplesap opened this issue Feb 11, 2015 · 11 comments
Assignees
Labels

Comments

@maplesap
Copy link

The document says:

_csrf parameter in req.body generated by the body-parser middleware.

From the client, I have done a POST and the request payload is:

{"email":"testing@blah.com","password":"asdfas","_csrf":"Cn72pjW6-8PQ43dGXIAjslG488tFfAAgzX0s"}

But I always get a 403 error "session has expired or tampered with".

This is the expressjs app:

var express = require('express');
var app = express();
var http = require('http').Server(app);
var cookie = require("cookie-session");
var session = require("express-session");

app.use(cookie('keyboard cat'));
app.use(session({
 secret: 'keyboard cat'   
}));

var csrf = require('csurf');
var bodyParser = require('body-parser');

app.use(csrf());
app.use(function(err, req, res, next) {
    if (err.code !== 'EBADCSRFTOKEN') return next(err);
    res.status(403).json({"error": "session has expired or tampered with"});
});

app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.post("/register", function(req, res) {
    var email = req.body.email;
    var password = req.body.password;

    console.log('login done');
    console.log(req.body);
    res.json({"done":"done"});
});

app.get("/register", function(req, res) {
    res.json({"csrf": req.csrfToken()});
});

http.listen(3000, function() {
    console.log('Express app started');
});

The csrf token is retrieved when the client does a GET /register, which sets the csrf token on the form. When the form is submitted, the request payload is sent but denied by csurf. I'm still not clear on what is needed after reading the doc. Am I missing something?

@dougwilson
Copy link
Contributor

You cannot use both cookie-session and express-session; they are mutually exclusive.

Simply remove one or the other and it works fine.

Your express-session is set up right, so if you remove the cookie-session, then it works. Your cookie-session is not setup correctly, so if you want to use that instead of express-session, then you'll need to fix that first.

@dougwilson dougwilson self-assigned this Feb 11, 2015
@maplesap
Copy link
Author

I removed the cookie-session, but it's still saying 403 Forbidden and that it's an invalid csrf token.

@dougwilson
Copy link
Contributor

Make sure that the cookies that are set when you call register you include in your response with the token.

@dougwilson
Copy link
Contributor

I'm pulling down and running your app now.

@dougwilson
Copy link
Contributor

The issue is to use the _csrf token in the body, your app.use(csrf()); needs to be after any bodyParser uses, otherwise the body hasn't been parsed before the check occurs.

@dougwilson
Copy link
Contributor

Clearly, the documentation needs to be expanded to explain that.

@dougwilson dougwilson reopened this Feb 11, 2015
@dougwilson dougwilson added docs and removed question labels Feb 11, 2015
@maplesap
Copy link
Author

Yes, that was the problem, thanks!

@ricardograca
Copy link

So, this means we can't use the "recommend way to use body-parser with express" anymore right?

@dougwilson
Copy link
Contributor

@ricardograca yes you can; you just need to move down this middleware into each of your routes, after the body parser, rather than using this module globally (or don't pass the token in the body, but in the URL, a header, or somewhere else). Example:

var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
var session = require('express-session')

// setup body parsing
var parseJson = bodyParser.json()
var parseUrlencoded = bodyParser.urlencoded()

// create a group
var parseBody = [parseJson, parseUrlencoded]

// setup csrf
var csrfProtection = csurf()

// setup express
var app = express()

// i put this _before_ express-session to not invoke session
// lookups for static files
app.use(express.static(__dirname + '/public'))

// user sessions
app.use(session({
 secret: 'keyboard cat'   
}))

app.post('/register', parseBody, csrfProtection, function (req, res) {
    var email = req.body.email
    var password = req.body.password

    console.log('login done')
    console.log(req.body)
    res.json({"done":"done"})
})

app.get('/register', csrfProtection, function (req, res) {
    res.json({"csrf": req.csrfToken()});
})

// handle csrf errors specifically
app.use(function(err, req, res, next) {
    if (err.code !== 'EBADCSRFTOKEN') return next(err);
    res.status(403).json({"error": "session has expired or tampered with"});
});

app.listen(3000)

@ricardograca
Copy link

@dougwilson That makes sense, many thanks.

@dougwilson
Copy link
Contributor

The readme should be clearer now :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants