Disable csrf validation for some requests on Express - node.js

I'm writing a small web app with Node.js using the Express framework. I'm using the csrf middleware, but I want to disable it for some requests. This is how I include it in my app:
var express = require('express');
var app = express();
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.cookieSession({secret: 'secret'}));
app.use(express.csrf());
I want to set a POST route without the csrf control.

There are several possible approaches. You basically need to understand what is the simplest and most correct rule to decide whether or not to use the csrf middleware. If you want csrf most of the time, except for a small whitelist of request patterns, follow the example in this answer I have about conditional logging middleware (copied below for convenience).
var express = require("express");
var csrf = express.csrf();
var app = express.createServer();
var conditionalCSRF = function (req, res, next) {
//compute needCSRF here as appropriate based on req.path or whatever
if (needCSRF) {
csrf(req, res, next);
} else {
next();
}
}
app.use(conditionalCSRF);
app.listen(3456);
Another approaches could be only using the middleware on a certain path like app.post('/forms/*', express.csrf()). You just want to find an expressive way to make it clean when the middleware will or will not be used.

Since Express middleware executes in order, you could always put your statements above the csrf() statement in the code.
Like this:
app.get '/ping', (req, res) -> res.status(200).end()
app.use csrf()
Express will return before your csrf token gets set. For very small numbers of endpoints (I just have one that fits this category), I've found this to be a cleaner solution.
Also, as of this writing, the code for the above answer would look like this:
customCsrf = (req, res, next) ->
if req?.url isnt '/ping'
return csrf()(req, res, next)
else
return next()
app.use customCsrf
That extra (req, res, next) tripped me up for awhile, so hope this helps someone.

dailyjs.com has a good article about csrf and express. It basically works like this:
use the csrf middleware:
app.configure(function() {
// ...
app.use(express.csrf());
// ..
});
create a custom middleware that sets the local variable token to the csrf value:
function csrf(req, res, next) {
res.locals.token = req.session._csrf;
next();
}
use your custom middleware in every route you want:
app.get('/', csrf, function(req, res) {
res.render('index');
});
in your form create a hidden field that holds the csrf value:
form(action='/contact', method='post')
input(type='hidden', name='_csrf', value=token)

Use the middleware at express app level adding every HTTP method to the ignored list, to ensure that the protection does not verify by default.
E.g.
const ignoredMethods = [
'GET',
'HEAD',
'POST',
'PUT',
'DELETE',
'OPTIONS'
]
const csrfInit = csurf({ignoredMethods, cookie: true });
app.use(csrfInit);
This will inject the csrfToken() method into each request object allowing the setup of a hidden field or consumable cookie by the application.
Then add a protected version in your as middleware to desired routes, and ignore the ones that do not require it.
E.g. Protected
const csrfProtection = csurf({ cookie: true });
router.post('/api/foo', csrfProtection, (req, res, next) => ...
E.g. Unprotected
const csrfProtection = csurf({ cookie: true });
router.post('/api/foo', (req, res, next) => ...
That worked for me.

const csrf = require("csurf");
const ROOT_IGNORE_LIST = ["/api/authenticate"];
const csrfMiddleware = csrf({
cookie: true,
ignoreMethods: ["GET", "HEAD", "OPTIONS"],
});
// Create middleware to check request url
app.use((req, res, next) => {
if (ROOT_IGNORE_LIST.includes(req.url)) {
next();
} else {
csrfMiddleware(req, res, next);
}
}).use("/api", routes);

Related

Passport.js: how to protect ALL routes?

I followed the documentation for passport.js with passport-local: http://www.passportjs.org/docs/authorize/
When I send my users to /login they are authenticated, but nowhere in that document can I find how to authorise my users.
I've tried this, but that gives me a bad request:
router.get('/somepage', passport.authenticate('local'), function(req, res, next) {
});
I'm looking for way to protect all my pages at once. I'm working with Express 4.16 and use different route files to split up my routes.
Sam
you can use middleware with a small trick to switch between strategies
example:
const allowUrl = ['public', 'nonprivate','home'];
const authenticationMiddleware = (whiteList =[]) => (req, res, next) => {
if(whiteList.find(req.baseUrl)) {
next();
}
if (req.isAuthenticated()) {
return next()
}
res.redirect('/');
}
app = express();
app.use(passort.initialize());
app.use(authenticationMiddleware(allowUrl));
app.use(apiRouter);
app.listen(3000, ()=> console.log('hello internet');
you can add your middleware code like below
router.get('/', isAuthenticated, function(req, res) {
//your next function
});
function isAuthenticated(req, res, next) {
// do any checks you want to in here
// CHECK THE USER STORED IN SESSION FOR A CUSTOM VARIABLE
// you can do this however you want with whatever variables you set up
if (req.user.authenticated)
return next();
// IF A USER ISN'T LOGGED IN, THEN REDIRECT THEM SOMEWHERE
res.redirect('/');
}
As I wanted ALL routes (except for login routes off course) to pass authorization, I solved it as follows:
var ensureAuthenticated = function(req, res, next) {
if (req.isAuthenticated()) return next();
else res.redirect('/login')
}
// usersRouter contains all open routes like '/login':
app.use('/', usersRouter);
// From here on, all routes need authorization:
app.use(ensureAuthenticated);
app.use('/', indexRouter);
app.use('/api/foo', fooRouter);
app.use('/api/bar', barRouter);
I'm not sure what do you mean by "but nowhere in that document can I find how to authorise my users". Passportjs won't authorize any user. It is an authentication middleware. Authorization is different from authentication.
I think you are looking for application level middleware. You have to use app.use to make authentication work for each request to the server.
You can read more about it here. https://expressjs.com/en/guide/using-middleware.html#middleware.application

Is it possible to have conditional routes in Expressjs?

What I want to do
if req is from mobile
first check these routes
router.use('/', mobileRoutes);
then ones below
router.use('/', routes);
but as far as I understand express goes through all use statements upon every request, it runs them all once and put route handlers into some sort of array. that get hit with requests.
For some urls I want to server different pages but no all (loginPage, registrationPage, etc).
Is it possible to do conditional routing, to prepend more routes in case some condition is met.
What I do have working is this:
router.use(function (req, res, next) {
let md = new MobileDetect(req.headers["user-agent"]);
if (md.mobile()) {
req.url = '/mobile'+req.url;
}
next();
});
//mobile routes
router.use("/mobile", require("./mobile"));
but it completely blocks all routes below.
I want a clean solution, One another option, which is not clean, is to add middleware to individual routes but that muddies code endlessly.
My Solution for conditional routing:
index.js
.....
//mobile routes
router.use("/", require("./mobile"));
router.get("/", require("./get"));
.....
mobile.js
const express = require("express");
const router = express.Router();
const MobileDetect = require("mobile-detect");
router.use(function (req, res, next) {
let md = new MobileDetect(req.headers["user-agent"]);
(md.mobile())? next() : next("router")
});
router.get("/", function (req, res) {
res.send({ query: req.query });
});
module.exports = router;

setting up a middleware in router.route() in nodejs (express)

what I want it to do.
router.post('/xxxx', authorize , xxxx);
function authorize(req, res, next)
{
if(xxx)
res.send(500);
else
next();
}
I want to check for session in each route.
But since the routers are written in this way.
router.route('/xxx/xxxx').post(function(req, res) {
// blah lah here...
//
});
So how can I set up a middleware that will check for session and I wanted to make things a bit more generic and wanted to have a single authorize function doing a single thing instead of checking in every request.Any suggestions.
Define a middlware function before you define / include your routes, this will avoid you checking for a valid session in every route. See code below for an example on how to do this.
If some routes are public, i.e. they do not require a user to have a valid session then define these BEFORE you 'use' your middlware function
var app = require("express")();
//This is the middleware function which will be called before any routes get hit which are defined after this point, i.e. in your index.js
app.use(function (req, res, next) {
var authorised = false;
//Here you would check for the user being authenticated
//Unsure how you're actually checking this, so some psuedo code below
if (authorised) {
//Stop the user progressing any further
return res.status(403).send("Unauthorised!");
}
else {
//Carry on with the request chain
next();
}
});
//Define/include your controllers
As per your comment, you have two choices with regards to having this middleware affect only some routes, see two examples below.
Option 1 - Declare your specific routes before the middleware.
app.post("/auth/signup", function (req, res, next) { ... });
app.post("/auth/forgotpassword", function (req, res, next) { ... });
//Any routes defined above this point will not have the middleware executed before they are hit.
app.use(function (req, res, next) {
//Check for session (See the middlware function above)
next();
});
//Any routes defined after this point will have the middlware executed before they get hit
//The middlware function will get hit before this is executed
app.get("/someauthorisedrouter", function (req, res, next) { ... });
Option 2 Define your middlware function somewhere and require it where needed
/middleware.js
module.exports = function (req, res, next) {
//Do your session checking...
next();
};
Now you can require it wherever you want it.
/index.js
var session_check = require("./middleware"),
router = require("express").Router();
//No need to include the middlware on this function
router.post("/signup", function (req, res, next) {...});
//The session middleware will be invoked before the route logic is executed..
router.get("/someprivatecontent", session_check, function (req, res, next) { ... });
module.exports = router;
Hope that gives you a general idea of how you can achieve this feature.
Express routers have a neat use() function that lets you define middleware for all routes. router.use('/xxxxx', authorize); router.post('/xxxx', 'xxxx'); should work.
Middleware:
sampleMiddleware.js
export const verifyUser = (req, res, next) => {
console.log('Verified')
next();
}
Routes
import express from 'express';
import { verifyUser } from './sampleMiddleware.js';
const userRoutes = express.Router();
userRoutes.route('/update').put(verifyUser, async function(){
//write your function heere
});
You've probably gotten the answer you need but I'll still drop this
router.route('/xxx/xxxx').get(authorize, function(req, res) {...});

require method called by function not working

I ran into this while setting some middleware only for certain routes. In my application, I only need session and csrf on my contact page.
Here's the problem. When I require them outside and use the references in an array as a route parameter, it works all fine. Session works fine and expects the same cookie(sessionID) for every request and CSRF check passes as it should.
But, when I use a function to return an array of 'middleware', then it behaves abnormally. When I 'get' contact, I receive one sessionID, and when I 'post' to it, apparently the session restarts and I get an error thrown since I have the older sessionID. And weirdly the CSRF also expects another csrf token and not the one it sent me.
I get that it's something to do with the way I'm requiring in modules, but I'd love to get a clear explanation as to why this is happening. Do note that I am beginner, so go easy on me :) Thanks!
// MIDDLEWARE FOR CERTAIN ROUTES
// This works fine!
var session = require('express-session')({secret: 'i lit fire to the rain', saveUninitialized: false, resave: true});
var csrf = require('csurf')();
router.get('/contact', [session, csrf], function(req, res, next) {
});
router.post('/contact', [session, csrf], function(req, res, next) {
});
// but this does not work
var contactMiddleware = function() {
var session = require('express-session')({secret: 'i lit fire to the rain', saveUninitialized: false, resave: true});
var csrf = require('csurf')();
return [session, csrf];
};
router.get('/contact', contactMiddleware(), function(req, res, next) {
});
router.post('/contact', contactMiddleware(), function(req, res, next) {
});
Hi have you tried to do this instead :
router.get('/contact', contactMiddleware, function(req, res, next)
edit:
so you only need a static array as far as I can see, so why not just doing this
var array = (function() {
var session = require('express-session')({secret: 'i lit fire to the rain', saveUninitialized: false, resave: true});
var csrf = require('csurf')();
return [session, csrf];
})();
and then passing your array to the route?
router.get('/contact', array, function(req, res, next) {
res.send('contact');
});

Chaining multiple pieces of middleware for specific route in ExpressJS

I want to just verify something but have't been able to find anything in the Express docs or online regarding this (although I know it's a feature).
I could just test this out but I don't really have a nice template and would like to hear from the community.
If I define a route in express like such:
app.get('/', function (req, res) {
res.send('GET request to homepage');
});
I can also define a middleware and load it directly, such as
middleware = function(req, res){
res.send('GET request to homepage');
});
app.get('/', middleware)
However, I can also chain at least one of these routes to run extra middleware, such as authentication, as such:
app.get('/', middleware, function (req, res) {
res.send('GET request to homepage');
});
Are these infinitely chainable? Could I stick 10 middleware functions on a given route if I wanted to? I want to see the parameters that app.get can accept but like mentioned I can't find it in the docs.
Consider following example:
const middleware = {
requireAuthentication: function(req, res, next) {
console.log('private route list!');
next();
},
logger: function(req, res, next) {
console.log('Original request hit : '+req.originalUrl);
next();
}
}
Now you can add multiple middleware using the following code:
app.get('/', [middleware.requireAuthentication, middleware.logger], function(req, res) {
res.send('Hello!');
});
So, from the above piece of code, you can see that requireAuthentication and logger are two different middlewares added.
It's not saying "infinitely", but it does say that you can add multiple middleware functions (called "callbacks" in the documentation) here:
router.METHOD(path, [callback, ...] callback)
...
You can provide multiple callbacks, and all are treated equally, and behave just like middleware, except that these callbacks may invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to perform pre-conditions on a route then pass control to subsequent routes when there is no reason to proceed with the route matched.
As you can see, there's not distinction between a middleware function and the function that commonly handles the request (the one which is usually the last function added to the list).
Having 10 shouldn't be a problem (if you really need to).
Express version "express": "^4.17.1" or above
From the document: Series of Middleware
var r1 = express.Router();
r1.get('/', function (req, res, next) {
next();
});
var r2 = express.Router();
r2.get('/', function (req, res, next) {
next();
});
app.use(r1, r2);
Let's try a real life example:
tourController.js
exports.checkBody = (req, res, next)=>{ // middleware 1
if (!req.body.price){
return res.status(400).json({
status:'fail',
message:'Missing price!!!'
})
}
next();
}
exports.createTour = (req, res) => { // middleware 2
tours.push(req.body);
fs.writeFile(
`${__dirname}/dev-data/data/tours-simple.json`,
JSON.stringify(tours),
(err) => {
res.status(201).json({
status: 'success',
data: {
tour: newTour,
},
});
}
);
};
tourRouter.js
const express = require('express');
const tourController = require('./../controller/tourController')
const router = express.Router();
router.route('/')
.get(tourController.getAllTours)
.post(tourController.checkBody, tourController.createTour);
//muliple Middleware in post route
module.exports = router //need this or the following step will break
app.js
const express = require('express');
const tourRouter = require('./route/tourRouter');
const app = express();
app.use(express.json());
app.use('/api/v1/tours', tourRouter);
module.exports = app;

Resources