8

I've tried to get csurf to work but seem to have stumbled upon something. The code so far looks like this:

index.ejs

<form method="post" action="/">
            <input type="hidden" name="_csrf" value="{{csrfToken}}">
            .
            .
</form>

Where you insert password and username in the form.

app.js

   var express = require('express');
var helmet = require('helmet');
var csrf = require('csurf');
var path = require('path');
var favicon = require('serve-favicon');
var flash = require('connect-flash');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');


var routes = require('./routes/index');
var users = require('./routes/users');
var profile = require('./routes/profile');

var app = express();


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

app.use(logger('dev'));

//Security shyts

app.use(helmet());
app.use(helmet.xssFilter({ setOnOldIE: true }));
app.use(helmet.frameguard('deny'));
app.use(helmet.hsts({maxAge: 7776000000, includeSubdomains: true}));
app.use(helmet.hidePoweredBy());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.noCache());

// rest of USE
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({secret: 'anystringoftext', saveUninitialized: true, resave: true, httpOnly: true, secure: true}));
app.use(csrf()); // Security, has to be after cookie and session.
app.use(flash());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use('/users', users);
app.use('/profile', profile);


// catch 404 and forward to error handler

app.use(function (req, res, next) {
  res.cookie('XSRF-TOKEN', req.csrfToken());
  res.locals.csrftoken = req.csrfToken();
  next();
})

//app.use(function(req, res, next) {
//  var err = new Error('Not Found');
//  err.status = 404;
//  next(err);
//});

// error handlers

// 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; 

Where I've put csrf after session and cookie parser.

index.js

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'some title',message: '' });
});

router.post('/',function(req,res){
// Where I have a bunch of mysql queries to check passwords and usernames where as if they succeed they get:
res.redirect('profile');
// Else:
res.redirect('/');
 });

What I get after submiting the form, no matter if I insert the correct username and password or not I still get the same error:

invalid csrf token

403

ForbiddenError: invalid csrf token

Also I want add that I've been working with node for about 2 weeks, so there is still alot I need to learn probably.

3
  • Are your routes being setup before or after the csurf middleware? It would help if you showed the complete code...
    – James
    Commented Aug 8, 2015 at 13:57
  • Guess I could print out the whole app.js part. Done!
    – Drwk
    Commented Aug 8, 2015 at 14:07
  • Silly question, but you are definitely binding the token to the form? I don't see that happening anywhere in your example.
    – James
    Commented Aug 8, 2015 at 14:28

3 Answers 3

13

{{csrfToken}} isn't an EJS construction, so it's not expanded at all and is probably sent literally to your server.

This should work better:

<input type="hidden" name="_csrf" value="<%= csrfToken %>">

The middleware is setting csrftoken though, with lowercase 't', where the template expects an uppercase 'T':

res.locals.csrftoken = req.csrfToken(); // change to `res.locals.csrfToken`

You also generate two different tokens, which is probably not what you want. Store the token in a variable and reuse that:

app.use(function (req, res, next) {
  var token = req.csrfToken();
  res.cookie('XSRF-TOKEN', token);
  res.locals.csrfToken = token;
  next();
});

And lastly, you probably have to move your middleware to before the route declarations, otherwise it won't be called:

app.use(function (req, res, next) {
  var token = req.csrfToken();
  res.cookie('XSRF-TOKEN', token);
  res.locals.csrfToken = token;
  next();
});
app.use('/', routes);
app.use('/users', users);
app.use('/profile', profile);
6
  • I'll try and report back!
    – Drwk
    Commented Aug 8, 2015 at 14:39
  • No errors and everything worked! I'll proceed and read up on more specifics around how tokens gets passed and so on. I however bow down and than you!
    – Drwk
    Commented Aug 8, 2015 at 14:47
  • why you say he generates the token twice? he creates one, and assigns it two times var token = req.csrfToken(); res.cookie('XSRF-TOKEN', token); res.locals.csrfToken = token; Commented Jun 30, 2021 at 15:51
  • 1
    @AndreaScarafoni if you look at the code in the original question, you'll see that it calls req.csrfToken() twice. The code I posted (which you're referring to) is a possible solution for that issue.
    – robertklep
    Commented Jun 30, 2021 at 16:19
  • 1
    @AndreaScarafoni no worries :D I edited my answer because it wasn't really clear that I meant the code to be a solution to the problem.
    – robertklep
    Commented Jul 2, 2021 at 17:12
2

My Express version is 6.14.4. The most important matter is you have to maintain the order of the line. App.js

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

//order of bellow lins is very important
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(csrf({ cookie: true }))

routes/index.js

var express = require('express');
var router = express.Router();

router.get('/user/signup', function(req, res, next){
  console.log("csruf: "+req.csrfToken());
  res.render('user/signup', { csrfToken: req.csrfToken() });
});

router.post('/postuser', function(req, res, next){
 //res.render('/');
 res.redirect('/');
});

view file

 <form action="/postuser"  method="POST">
            <div class="form-group">
                <label for="email"> E-Mail</label>
                <input type="text" id="email" name="email" class="form-control">
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" name="password" class="form-control">
            </div>
            <input type="hidden" name="_csrf" value="{{csrfToken}}">
            <button type="submit" class="btn btn-primary">Sign Up</button>
   </form>
0

I was facing this issue, because I Have not included following middleware. It worked well after that.

app.use(express.urlencoded({ extended: true }));

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.