Coder Social home page Coder Social logo

csurf's Introduction

csurf

Deprecated

This npm module is currently deprecated due to the large influx of security vulunerability reports received, most of which are simply exploiting the underlying limitations of CSRF itself. The Express.js project does not have the resources to put into this module, which is largely unnecessary for modern SPA-based applications.

Please instead use an alternative CSRF protection package if you do need one: https://www.npmjs.com/search?q=express%20csrf

csurf's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

csurf's Issues

Can docs clarify how cookie mode works?

I know some (including me) have had confusion about the difference between the csrf cookie placed in the client and the token submitted in the requests.

I understand it to work the following way:

  1.  Uses a random value as a secret to create a “token”
  2.  Stores this random value secret as a cookie (this is the csrf cookie we see in the client's browser)
  3.  We take the “token” created from this secret and store it as a hidden value in a form.
  4.  When the form is submitted, the CSURF middleware takes the “token” submitted with the form via a hidden field and compares it with the secret stored in the csrf cookie.
  5.  If it doesn’t match (after de-hashing?), it says the CSRF token is invalid and rejects the request.

If this is the case, is there anyway to test a CSRF protected POST route via something like Postman? and if so, does anyone have suggestions for this?

I've looked at some answers such as this here:
https://stackoverflow.com/questions/27182701/how-do-i-send-spring-csrf-token-from-postman-rest-client/35925413

But they don't seem to work as described.

Invalid csrf token when calling req.session.destroy()

When using kue.app.listen (and the kue module) I noticed the following issue and reported to @dougwilson in #express who told me to open the issue here.

When csurf() is hit, it reads the stored secret in the session, and after that, you can't get it to use another secret, so destroying the session will invalidate whatever csurfToken() gives, even when it is after the req.session.destroy() call.

This only seems to be happening when I use kue, so I thought it was an issue with kue and reported it here: Automattic/kue#368

Upgrade to [email protected] for SameSite=None support

Currently the following code:

    const csrf = csurf({
        cookie: {
            key: '_csrf',
            httpOnly: true,
            secure: true,
            sameSite: 'none',
        },
    });

Throws the following error:

express_error { error:
   TypeError: option sameSite is invalid
       at Object.serialize (/home/me/myapp/node_modules/cookie/index.js:174:15)
       at setCookie (/home/me/myapp/node_modules/csurf/index.js:246:21)
       at setSecret (/home/me/myapp/node_modules/csurf/index.js:275:5)
       at csrf (/home/me/myapp/node_modules/csurf/index.js:107:7)
       at /home/me/myapp/mytest.js:117:9

I believe this is because [email protected] does not support none as a valid value for the sameSite option. [email protected] has added this support [1].

Other projects that depend on cookie have upgraded to [email protected]. For example, express-session [2].

SameSite=None is a valid cookie attribute [3] and with the change in Chrome 80 in how SameSite is defaulted [4], setting SameSite=None is a needed feature in csurf.

[1] https://github.com/jshttp/cookie/releases/tag/v0.4.0
[2] https://github.com/expressjs/session/releases/tag/v1.17.0
[3] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
[4] https://www.chromium.org/updates/same-site

Feature add 'Encrypted Token Pattern'

I think that adding the 'Encrypted Token Pattern' for stateless CSRF protection would be a good feature to add as an alternative to 'double submit.' #

When session store is down, csurf should call next(err), not throw error

This is a deviation from recommended patterns for connect-based middleware. When a middleware encounters an unrecoverable error, it should call next(err).

This is not the case below (index.js). Also the error message is misleading in this case because the configuration is valid:

throw new Error('misconfigured csrf')

Also this does not filter page and xhr requests versus resources. The end result is that resources like styles and images cannot be served to build a friendly nice looking error page.

Unable to get Angular and Express to use csrf

I'm having some trouble getting both these frameworks to use and verify csrf. Main problem is that angular's csrf is always undefined and csurf doesn't seem to verify any tokens at all (the app.post('/login') is always run successfully as though csurf was never integrated).

Here is my attempt at getting a register form to use csrf:
https://github.com/maplesap/csrf_test

You can run it with node app.js and then visit /register to see the problem. Can anyone help?

Make the docs actually useful.

Currently the readme basically says nothing.

I'll have a go at it, but I don't know much about csurf and I have had some trouble implementing it in the past.

EBADCSRFTOKEN after session expirery

Please note in the docs, that if using the cookie-session module, it must be required before requiring csurf.

My code looked like this:

....
var cookieParser = require('cookie-parser');
var csrf = require('csurf');
var bodyParser = require('body-parser');
var express = require('express');
var cookieSession = require('cookie-session'); // <- should be required before csurf!
...
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(cookieSession({
    secret: conf.session.secret,
    expires: new Date(Date.now() + sessionMaxAge),
    key: 'sessionId'
}));
app.use(csrf({ cookie: true}));
...

I passed the csrf token to the view:

res.render('index', { csrf: req.csrfToken() });

In the view I use the csrf token in a hidden input element (Jade code):

input(type='hidden', name='_csrf', value='#{csrf}')

Now if I waited until the session had expired and tried to submit the form I got the error EBADCSRFTOKEN no matter what.

Requiring cookie-session before csurf solved the problem for me.

res.cookie is undefined with connect 3

Dear All,

I just switched to connect 3 and i have an issue, hoped you can help me out.

var connect = require('connect'),
cookieParser = require('cookie-parser'),
cookieSession = require('cookie-session'),
csrf = require('csurf')
...
var app = connect()
    .use( cookieParser( process.env.SEC_SESSION_PASS || global.config.server.session.hashSecret ) )
    .use( cookieSession( {
        name:  text'.sid',
        secret: 'secret',
        cookie: { httpOnly: true }
    } ) )
    .use( csrf( { cookie: { key: 'XSRF-TOKEN', httpOnly: true } } ) )
...

And to all request I send I received this:

/.../node_modules/csurf/index.js:65
    res.cookie(cookieKey, secret, cookie);
        ^
TypeError: undefined is not a function
    at /.../node_modules/csurf/index.js:65:13
    at Object.ondone (/.../node_modules/csurf/node_modules/csrf-tokens/node_modules/uid-safe/index.js:13:7)

All packages are the latest ones available in NPMJS.

Tanks in advance!

Weird _csrf cookie issue in safari.

I have this odd issue with the _csrf cookie in safari (not sure if this is csurf library or not).

I can replicate this in my app by loading up "https://crm-node:3443/tickets" with no cookie and it decides to set 2.

I use this setup code:

    app.use require('express-session')
        cookie:
            httpOnly: true
            secure: true
            maxAge: 3600000 * 8 # hour in ms * 8 = 8 hours
        secret: security.cookieSecret
        store: sessionstore
        saveUninitialized: true
        resave: true
    csrf = require "csurf"
    app.use csrf cookie: true
    app.use (req, res, next) ->
        res.cookie 'XSRF-TOKEN', req.csrfToken()
        next()
    # error handler
    app.use (err, req, res, next) ->
        if err.code isnt 'EBADCSRFTOKEN' then return next err

        # handle CSRF token errors here
        res.status 403
        res.send 'session has expired or form tampered with'

And looking on safari for some reason I get the following, causing errors for some bizarre reason:
2015-02-13_1701

Note the duplicate _csrf cookie, one for '/' and one for '/tickets'.

Is this a bug or some kind of user error?

Random 'misconfigured csrf' exception thrown

Hello,

Yesterday, our site started giving all users an http500 error, which we tracked back to csurf failing with the error message 'misconfigured csrf at getSecret'. The site had been up and running for weeks, and this error started getting reported out of the nowhere. Restarting the server brought the site back to normal.
Our site runs csurf 1.7.0, just wanted to file this issue and check if other users have been experiencing the same situation.
Unfortunately, I am not able to repro the issue.

Custom error type

Would be nice to catch the error in a middleware error handler without having to parse the message. Something like:

/**
 * Verify the token.
 *
 * @param {IncomingMessage} req
 * @param {Object} tokens
 * @param {string} secret
 * @param {string} val
 * @api private
 */

var InvalidCsrfToken = function(message) {
    this.name = "InvalidCsrfToken";
    this.message = message || "Invalid CSRF token.";
};
InvalidCsrfToken.prototype = new Error();
InvalidCsrfToken.prototype.constructor = InvalidCsrfToken;

function verifytoken(req, tokens, secret, val) {
    // valid token
    if (tokens.verify(secret, val)) {
        return;
    }

    var err = new InvalidCsrfToken();
    throw err;
}

I don't think that the verifytoken function should throw a 403 (as it currently does) as this should be handled by the error handler, although if it's best left here that's understandable. It can always be changed by the error handler.

Just to demonstrate, my error handler could then...

app.use(function(err, req, res, next) {
    // Log all errors.
    console.error(err);

    // Pull the default error message from the error, updating it where helpful.
    var message = err.toString();

    // Send the proper response code.
    switch (err.name) {
        case 'ValidationError':
        case 'BadRequest':
            res.status(400);
            break;
        case 'InvalidCsrfToken':
            res.status(403);
            message = "The form you submitted has become outdated or has been tampered with. Foo bar baz.";
            break;
        case 'NotFound':
            res.status(404);
            break;
        default:
            res.status(500);
            message = "An internal error occured.";
            break;
    }

    // Send the error.
    if (req.xhr || req.get('content-type') == 'application/json') {
        res.json({error: message});
    } else {
        res.render('pages/error', {error: message});
    }
});

And everyone wins.

Need docs and examples for working with single page application.

For now, the docs give two examples. Both of them are based on server-side render.

The key point is: how to send csrf token to front-end domain.

back-end server will not render a <form> or index.html page so you can attach csrf-token to hidden form field or <meta> tag.

We also can't provide a api to grab the csrf-token.

back-end: http://localhost:3000
front-end: http://localhost:4200

I search a lot, but still do not find a solution for this case.

Add ignore URL patterns (like options.ignoreMethods)

Scenario:
All my site forms protected by csrf - it is ok.
But url /special must receive POST request from other site (example, self-signed notification of payment) and there is no csrf available for this request.

Example:

app.use(csurf({ignoreUrls: new RegExp('^/(special|nocsrf)')}));

And in library:

    // verify the incoming token
    if (!ignoreMethod[req.method] && !req.url.match(ignoreUrls)) {
      verifytoken(req, tokens, secret, value(req))
    }

ignoreRoutes as extension of ignoreMethods

hey there.
I'm currently making use of the csurf module and having an issue, which I think is interesting for others too and can be fixed very easy:
I do want to use and valide csurf for every method, also for get, head, options, etc. - but I want to specifiy some specific routes, which should not be validated.
My quickfix was to fork your module on my private npm and include the following:

var ignoreRoutes = options.ignoreRoutes === undefined
? []
: options.ignoreRoutes;

if (!ignoreMethod[req.method] &&
ignoreRoutes.indexOf(req.path.replace(new RegExp("/", "g"), "")) === -1) {
verifytoken(req, tokens, secret, value(req));
} 

Now I could use the middleware that way:

var csrfExcludedRoutes = ["someRoute"];

this.app.use(csrf({cookie: true, httpOnly: false, ignoreMethods: [], ignoreRoutes: csrfExcludedRoutes}));

I want to get the csrf-token created, but not validated. Is there another way to do that?

Token Lifetime

Can I set the Token Lifetime ? and whats is the default lifetime ?

No regeneration of secret when a valid token is submitted

I found that this module only checks whether the submitted CSRF token is valid against the secret inside session/cookie secret at this line https://github.com/expressjs/csurf/blob/master/index.js#L111.

However, when a correct token is submitted, and it is verified, the code does nothing to the secret. So, I can reuse the same token over-and-over again because the secret does not change.

What it should do is when a valid token is verified, it should regenerate a new secret.

TypeError: Bad input string - Hash.update

In index.js

  var hash = escape(crypto
    .createHash('sha1')
    .update(salt)
    .update('-')
    .update(secret)
    .digest('base64'))
  return salt + '-' + hash

it looks like .update("-") causes a TypeError: Bad input string error.
It seems to work find when i remove that line. I'm wondering if it's an issue with an older version of crypto? since it seems to work find when i pull up a node console.
Any thoughts?

previous token still valid

For every request it will generate new token but the old token is still valid if I make the request with old token there is no CSRF error is showing.

app.use(function(req, res, next) {
  console.log("Token",req.csrfToken());
  res.setHeader('X-CSRFTOKEN',req.csrfToken())
  next();
});

Cannot validate CSRF token using the example code

I am unable to use csurf in this basic test application, no matter what I do I always get "ForbiddenError: invalid csrf token" after sending a form.

What am I doing wrong? I am trying to follow the example given in the documentation for csurf (although I am using TLS as well - could that affect the results?).

'use strict'

var https = require('https');
var express = require('express');
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var fs = require('fs');
var oauth = require('./oauth.js');
var argv = require('minimist')(process.argv.slice(2), {
    alias: {p: 'port'},
    default: {port: 80},
});

// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })

var app = express();

app.use(cookieParser());

// Init SSL
var sslPath = '/etc/letsencrypt/live/censored/';
var options = {
    key: fs.readFileSync(sslPath + 'privkey.pem'),
    cert: fs.readFileSync(sslPath + 'fullchain.pem')
};

app.get('/some-form', csrfProtection, function(req, res){
        res.send('<form action="/process" method="POST">' +
                    '<input type="hidden" name="_csrf" value="' + req.csrfToken() + '">' +
                    'Favorite color: <input type="text" name="favoriteColor">' +
                    '<button type="submit">Submit</button>' +
                    '</form>');
});

app.post('/process', csrfProtection, function(req, res){
        res.send('<p>Your favorite color is "' + req.body.favoriteColor + '".');
});


app.get("/", function(req, res) {
    res.send('Hello World!');
});
app.listen(argv.port, function() {
    console.log('listening on port ' + argv.port);
});

https.createServer(options, app).listen(443);

err does not have property 'code'

Hi.

I'm trying to use the example if (err.code !== 'EBADCSRFTOKEN') return next(err) - I stop the app, then the restart it and try to send the form which contains a csrf hidden field,
but my app (Express 4.x) returns such an err:

{ [Error: invalid csrf token] status: 403 }

which is really strange, and I cannot even get the error string.

Any ideas?

Disable CSRF checking during tests

Currently I'm trying to test my expressjs POST routes using Mocha/Chai. Without manual intervention all post requests fail with such a trace:
POST /users/new ForbiddenError: invalid csrf token at csrf (/vagrant/node_modules/csurf/index.js:112:19) at Layer.handle [as handle_request] (/vagrant/node_modules/express/lib/router/layer.js:95:5)
So I searched around and found this this answer from which I wrote this in my tests:

let should = chai.should()
// preserve token and agent
let csrfToken, agent

before((done) => {
    agent = chai.request(server)
    agent.get("/_csrf")
        .end((err, res) => {
            csrfToken = res.body.csrf
            done()
        })
})

// the test
it("should create a user when valid user data is posted", (done) => {
        agent.post("/users/new")
            .send({ username: "validUserName", password1: "123456", password2: "123456", _csrf: csrfToken })
            .end((err, res) => {
...

So I'm passing the token (which I checked to be a real value) but the test is still failing. Can somebody tell me why this is not working?

Me having used django, I never had to deal with the problem because CSRF checks are disabled by default during tests. Is this something that can (should) be achieved with csurf? Like disable CSRF if NODE_ENV == "test"?

A cookie secret is not really secret

Am I understanding correctly that the cookie option stores the secret that is used to create the csrf token as a cookie?

If so, the attacker could change the cookie and thereby always send a valid token.

I think this only works if CORS is disabled or there's an XSS vector (in which case it doesn't really matter), but it could be mitigated by using an extra fixed secret in the server that combines with the cookie secret?

Add credentials warning to documentation

When doing a POST or PUT request with the new-ish fetch() API:

fetch('/URL', {
  method: 'PUT',
  body: 'whatever',
  headers: { 'csrf-token': csrf }
}).then(res => res.json()).then(res => {
  console.log(res);
});

I receive a typical error of:

ERROR { ForbiddenError: invalid csrf token
...
code: 'EBADCSRFTOKEN' }

After many hours searching the web and debugging it (since it is a hard-to-debug issue if you don't know why), I found out that this is because fetch() does not send the current session. This can be solved adding the credentials: 'include' key-value pair to the fetch() request:

fetch(`/todo/${e.target.id}`, {
  method: 'PUT',
  body: done,
  credentials: 'include',
  headers: { 'csrf-token': csrf }
}).then(res => res.json()).then(res => {
  console.log(res);
});

However Google returns really bad results for this as there are many similar errors. I think more people might be having this issue and it'll be more and more common as fetch() becomes more popular, and it's not something that will be easy to debug for people new to csurf.

So I propose to add a warning to the documentation, since fetch is an standard and it doesn't work without adding this (it creates a new session).

Question: any harm in setting res.locals.csrfToken?

Is there any harm with doing this so I can avoid having to put the csrfToken: req.csrfToken() in each res.render(...) in my Express app.

app.use(function(req, res, next) {
  res.locals.csrfToken = req.csrfToken();
  next();
});

Thanks

Expose token validation function

It can be useful csrf tokens as a state parameter to oauth callbacks. Those are a bit of a snowflake: the state is expected to be part of the query of an otherwise normal GET request.

It would be great if something along the lines of this would work:

app.get('/oauth/callback', function(req, res, next) {
  req.csrfToken.verify(req.query.state, function(err) {
    if (err) return next(err);
    // ... react to token that is passed into the callback ...
    res.redirect('/actually-do-stuff');
  });
});

Disable it for API routes

My node application routes the requests in two main categories /app, /api. The application uses csurf as described in the examples.

How can I disable it for the /api routes since I'm using token authentication (RFC 6750) and I don't want CSRF protection.

separating token creation and checking

Currently csrf secret creation and token checking are done "together." The problem is that I may want to put the csrf secret creation early on in the middleware stack, but then check the token some time later in the stack.

My use case is that I only parse incoming form submissions in routes where I expect them instead of parsing them near the top of the stack for every request. The form is where I'd usually have my csrf token for verification.

Cookie csurf doesn't work

I can't reopen #40, but I can reproduce the problem easily:

    var app = require('express')()
    app.use(require('cookie-parser')())
    app.use(require('csurf')({cookie: true}))

    // error handler
    app.use(function (err, req, res, next) {
        if (err.code !== 'EBADCSRFTOKEN') return next(err)

        // handle CSRF token errors here
        res.status(403)
        res.send('session has expired or form tampered with')
    })

    require('http').createServer(app).listen(3000);

I run that as node app.js and then test it with curl:

$ curl -v 'http://localhost:3000'

< set-cookie: _csrf=DkSxyeny6EX0rBL205t26k00

$ curl -v -H 'Cookie: _csrf=DkSxyeny6EX0rBL205t26k00' -H 'X-XSRF-TOKEN: DkSxyeny6EX0rBL205t26k00' --data-binary '{}' 'http://localhost:3000'

> Cookie: _csrf=DkSxyeny6EX0rBL205t26k00
> X-XSRF-TOKEN: DkSxyeny6EX0rBL205t26k00

< HTTP/1.1 403 Forbidden

Safari 7.04 403 "invalid csrf token"

I am using express 4.3.1 and csurf and everything works on chrome no problem.

However in Safari every form post generates a 403 "invalid csrf token" error.

Items of note:

  • Everything is over https
  • I am running on Nodejitsu where they terminate ssl and forward traffic to the site w/o encryption.
  • I have app.enable('trust proxy'); in place
  • Cookies are set to secure
  app.use(session({
    secret: config.session.secret,
    key: 'sessionId',  // Use something generic so you don't leak information about your server
    cookie: {
      httpOnly: true,  // Reduce XSS attack vector
      secure: true,    // Cookies via SSL
      maxAge: config.session.maxAge
    },
    store: new MongoStore({
      mongoose_connection: db,
      auto_reconnect: true
    })
  }));

Safari works in development without SSL and without secure cookies just fine.

I'm stumped. Anyone have any ideas?

When csrfToken() method is called?

Hello,

I had arranged a middleware to allow a certain route to pass without csrf examine like the below. Now, I've been trying to get rid of this error message: [TypeError: Object #<incomingMessage> has no method 'csrfToken'] . Because of this error my response status is set as 500 (internal server error).

I was wondering how can I obstruct being called csrfToken() ?

var csrf = csurf()
app.use(function (req, res, next) {
    if (req.path == '/test') return next()        
    csrf(req, res, next)          
}) 

BREACH attack mitigation

I don't see any issues regarding lack of support for prevention against BREACH attack. Is it supported?

rails/rails#16570

An implementation that supposedly is a work around that.

Invalid Token when using 'Ignoring Routes' example

When using the first example in the Readme, (using Ejs template language), the token validation works fine. When I try using the 'Ignoring Routes' example, on the 'GET /form' to 'POST /process' execution(just as I did in the first example), I get 'invalid token' on the 'POST'. The token is being passed to the form on the GET. Any ideas?

Add an example of usage including a form

I believe it's not very clear what's the way of using it.
Adding an example with a form will help for it. In the current documentation only the server side is shown.

I found this example in stackoverflow which shows how to use it in the views as well:

app.use(require('body-parser')());
app.use(require('cookie-parser')('YOUR SECRET GOES HERE'));
app.use(require('express-session')());

app.use(require('csurf')());

app.get('/some-form', function(req, res){
    res.send('<form action="/process" method="POST">' +
        '<input type="hidden" name="_csrf" value="' + req.csrfToken() + '">' +
        'Favorite color: <input type="text" name="favoriteColor">' +
        '<button type="submit">Submit</button>' +
        '</form>');
});

app.post('/process', function(req, res){
    res.send('<p>Your favorite color is "' + req.body.favoriteColor + '".');
});

The use of csrfToken() is not explained in the docs.

I don't understand how this module works.

Or rather, I don't know how to use it :)

Basically, I want the following:

  1. When a user registers or logs into my app I want to set a header with an Anti-CSRF token.
  2. When a user goes to any other route I want to verify this token.

So I set the following code into my server:

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.use(function (req, res, next) {
  res.cookie('XSRF-TOKEN', req.csrfToken());
  next();
});

// unauthenticated routes
var authCtrl = require('./routes/auth');
app.use('/auth/login', authCtrl.login);
app.use('/auth/register', authCtrl.register);

// verify JWT token for authorization
app.use(require('./middleware/access_token').checkJwt);

// authenticated routes
var usersCtrl = require('./routes/user');
app.get('/users', node_acl.customMiddleware, usersCtrl.getAll);

// ... more routes ...

// global error handler
app.use(function (err, req, res, next) {
  console.error(err);
  if (err.code == 'EBADCSRFTOKEN') {
    err.status = 403;
    err.message = 'Invalid Anti-CSRF token.';
  }
  res.status(err.status || 500).send(err.message || 'Internal server error.');
});

The problem is the following: the line that goes app.use(csrf({ cookie: true })); already tries to validate the token. But I cannot remove it, because then this code req.csrfToken() would fail because csrfToken() is undefined.

Am I missing something here? How do I attach csrfToken() to req without actually trying to validate it?

how to handle csurf function not being available on req object when a session store goes down

Wondering your thoughts on this. recently, my redis provider went down for a sec, and sent my app into a downward spiral of death. i've since added in better error handling for when the session isn't available, and also a check to see if first the req.csrfToken() function exists, so the app won't crash. is that pretty much the extent of it?

if (req.csrfToken) {
        state.csrf = req.csrfToken();
      } else {
        next(new Error('no csrftoken function'));
      }

and for sessions:

app.use(function (req, res, next) {
    if (!req.session) {
      return next(new Error('session not defined'));
    }
    next();
  });

per-page CSRF token support

Currently we implement the CSURF in our project to add security feature.

Here how we implement it :

under routes

/** Implement CSRF Token */
var csrfProtection = csrf();

/** Home page */
app.get('/user', isAuthenticated, csrfProtection, home.show);

app.post('/new/user', isAuthAPI, csrfProtection, user.update);

Add the token in meta data

<meta name="csrf-token" content="{{_csrftoken}}">

Then override AJAX to add the token

/** SET CSRF */
var CSRF_HEADER = 'X-CSRF-Token';

var setCSRFToken = function (securityToken) {
  jQuery.ajaxPrefilter(function (options, _, xhr) {
    if (!xhr.crossDomain && options.type != 'get') {
      xhr.setRequestHeader(CSRF_HEADER, securityToken);
    }
  });
};

setCSRFToken($('meta[name="csrf-token"]').attr('content'));
/** END SET CSRF */

Then i try the a single token in all the page and it was working. It should be valid only in one page or one request ?

How to expire old csrf tokens?

In the document, the example given:

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

Shows that it will provide the form with a new csrf token whenever /form is requested.

If I refresh the form to get a new csrf token, but POST with the old csrf token, it won't throw a 403 error and will succeed.

Would it be good practice to expire old tokens as soon as they are used? How can that be done with csurf?

cookie option cannot be bool true or else .key is undefined...

docs say:

  • cookie set to a truthy value to enable cookie-based instead of session-based csrf secret storage.
    • If cookie is an object, these options can be configured, otherwise defaults are used:
      • key the name of the cookie to use (defaults to _csrf) to store the csrf secret

code says:

if (options.cookie && !options.cookie.key) {
    options.cookie.key = '_csrf'
}

so, if i do the following:

app.use(express.csrf({ cookie: true }));

options.cookie.key is undefined because .key is assigned to a bool.

what am I missing?

csrf always fails

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":"[email protected]","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?

A way of getting csrfToken through POST request

Here's the example from official docs, except one difference: xsrfToken is sent in response to POST request, not GET:


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

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

var app = express()
app.use(cookieParser())

app.post('/authenticate', /*csrfProtection,*/ function (req, res) {
    // check credentials from request.body
    // and then 

    res.send({ csrfToken: req.csrfToken() })  //EXCEPTION: csrfToken is not a function 
})

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

I'm facing the egg-hen problem: if I enable csrfProtection, I cannot access the endpoint without the token, but if I disable it, req.csrfToken becomes undefined.

I need the /authenticate endpoint to be POST, because I don't want to expose password as url parameter.

Allow Configuration for Session Key

Hi all!

I'm currently building an authentication library, which ships with pre-built registration and login forms. Because of this, my library uses this csurf module to protect the form inputs.

My library initializes it's own session management stuff (to maintain user state), using the node-client-sessions library from Mozilla (https://github.com/mozilla/node-client-sessions), which allows me to specify a custom request session key.

So for instance, I can use that library to initialize ONE session as req.stormpathSession, and another as req.session.

I like doing this because this way -- my library does its own session stuff under req.stormpathSession, while the user USING my library can create / do their own session stuff as req.session, which is sort of the convention.

Anyhow: back to my original point. What I'd like is for this ilbrary to support custom session keys. This way, I could tell the csurf middleware to write data to req.stormpathSession instead of the default req.session. This would make it possible for my sort of use case to work.

If this is cool with ya'll, and you'll merge it in, I'd be happy to submit a clean patch supporting this behavior along with full tests / etc.

Let me know if this sounds OK!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.