Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node js csrf token protection not working

I have the following in my app.js file server side after bodyParser

let dotEnv = require("dotenv");
dotEnv.load();
let express = require("express");
let app = express();
if (process.env.NODE_ENV === 'production') {
  app = require('./public/web/server').app;
}
let passport = require("passport");
let server = require("http").Server(app);
let io = require("socket.io").listen(server);
// load intial configuration
require("./startup/initial-configuration")(app, io);
require("./server/config/socket")(io);
require("./server/config/database")(app, process.env.NODE_ENV);
require("./server/authentication/passport.local")(passport);
require("./server/authentication/passport.impersonate");
require("./startup/initial-routes")(app);
if (process.env.NODE_ENV === 'production') {
  app.get('*.*', express.static('./public/web/browser', {
  maxAge: '1y'
}));
app.get('*', (req, res) => {
  res.render('index', {
   req,
   res
  }, (err, html) => {
    if (html) {
      res.send(html);
    } else {
      // console.error(err);
      res.send(err);
    }
   });
  });
 }
 require("./server/middleware/custom-middleware")(app);
 module.exports = { app: app, server: server };

As you can see, I have a file initial-configuration loaded inside app.js, the content of that file is:

const path = require("path");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const csurf = require("csurf");
const helmet = require("helmet");
const compression = require("compression");
const useragent = require("express-useragent");
const cors = require("cors");
const passport = require("passport");
const express = require("express");
const cookieMiddleware = require("../server/middleware/cookie-middleware");
const checkCSRFMiddleware = require("../server/middleware/checkCSRF-middleware");
const notificationModel = require("../server/model/notification/notification.model");
const logger = require("./logger");
const morgan = require("morgan");
module.exports = (app, io) => {
 app.set("case sensetive routing", true);
 if (process.env.NODE_ENV === "production") {
   app.enable("trust proxy");
 }
 app.use((req, res, next) => {
   res.io = io;
   res.header(
    "Access-Control-Allow-Headers",
    "X-CSRF-Token, Content-Type"
   );
  notificationModel.setIO(io);
  next();
 });

 let corsOption = {
   origin: true,
   methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
   credentials: true,
   exposedHeaders: ["x-auth-token"]
 };

 app.use(cors(corsOption));
 // app.use(logger('dev'));
 app.use(helmet());
 app.use(useragent.express());
 app.use(compression());
 app.use(bodyParser.json());
 app.use(
   bodyParser.urlencoded({
    extended: false
  })
 );
 app.use(cookieParser());
 app.use(cookieMiddleware);
 app.use(passport.initialize());
 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')
 })
 app.use(function (req, res, next) {
 res.cookie('XSRF-TOKEN', req.csrfToken())
 next()
})

// app.use(express.static(path.join(__dirname, "../public/web/browser")));
app.use(
 morgan("combined", {
   stream: logger.stream
 })
);
};

And in Angular, I have only imported following lines in app.module

HttpClientXsrfModule.withOptions({
  cookieName: "XSRF-TOKEN",
  headerName: "X-CSRF-TOKEN"
}),

And all of my requests header has Cookie:_csrf=TmghRq3eWC-PxQfp6pvuHw07; XSRF-TOKEN=vMPrZZtA--BgtY1YVqDRXmi5A6RSbMNb61JA

But all my post requests failed and says code: "EBADCSRFTOKEN".

Should I do something on the angular side? Should I append that with form data?

For any help thanks.

Here are my request details request details

my login request

like image 245
jones Avatar asked Dec 10 '18 12:12

jones


People also ask

Why CSRF token is not working?

Invalid or missing CSRF token This error message means that your browser couldn't create a secure cookie, or couldn't access that cookie to authorize your login. This can be caused by ad- or script-blocking plugins, but also by the browser itself if it's not allowed to set cookies.

How do you implement CSRF in node JS?

To implement CSRF tokens in Node. js, we can use the csurf module for creating and validating tokens. Lines of code in an integrated development environment. Notice that we added the routing code here, but you can have it in a separate routing class index.

How does Csurf work?

Csurf module in Node. js prevents the Cross-Site Request Forgery(CSRF) attack on an application. By using this module, when a browser renders up a page from the server, it sends a randomly generated string as a CSRF token. Therefore, when the POST request is performed, it will send the random CSRF token as a cookie.


1 Answers

In your code you are using many modules. To isolate the problem I suggest to reduce your code to a minimal version removing everything not mandatory to enable csrf.

This is only a suggestion, however In my Angular 7 application I added this (it's not required to change cookiename and token, because they have default values in Angular's sourcecode):

HttpClientModule,
HttpClientXsrfModule.withOptions()

then in my server (main file app.js) with Express 4 I added this code (in this exact order):

const csrf = require('csurf');
app.use(bodyParser.urlencoded({
  extended: false
}));
// then add cookie parser and csrf config
app.use(cookieParser());
app.use(csrf({
  cookie: {
    // here you can configure your cookie. Default values are ok, but I decided to be more explicit
    // http://expressjs.com/en/4x/api.html#req.cookies
    key: '_csrf',
    path: '/',
    httpOnly: false, // if you want you can use true here
    secure: false, // if you are using HTTPS I suggest true here
    signed: false, // I don't know if csurf supports signed cookies, so I used false
    // not mandatory, but if you want you can use sameSite: 'strict'
    // sameSite: 'strict', // https://www.owaspsafar.org/index.php/SameSite
    maxAge: 24 * 60 * 60 * 1000 // 24 hours
   }
}));
app.use((req, res, next) => {
  const csrfTokenToSendToFrontEnd = req.csrfToken();
  console.log('csrfTokenToSendToFrontEnd: ', csrfTokenToSendToFrontEnd);
  // this cookie must be XSRF-TOKEN, because already defined as default in Angular.
  res.cookie('XSRF-TOKEN', csrfTokenToSendToFrontEnd);
  next();
});

// here requires the api file with all your rest apis (not static paths)
const routesApi = require('./src/routes/index')(express, passport);
app.use('/api', routesApi);

And finally, before the end of the file (before the 500 middleware) I added this to handle errors:

// error handler
app.use((err, req, res, next) => {
  if (err.code !== 'EBADCSRFTOKEN') {
    return next(err);
  }
  res.status(403).json({
    message: 'error'
  });
});

I copied only relevant code. If you have issues feel free to ask, I'll check again in my code if I forgot something.

like image 144
Stefano Cappa Avatar answered Sep 30 '22 18:09

Stefano Cappa