Invalid csrf token issue express nodejs - node.js

I am trying to implement csrf tokens for the first time and i am running into issues. I've been working at it for a few hours and haven't been able to solve it. Below is the error I am getting:
ForbiddenError: invalid csrf token
app.js
const express = require('express')
const app = express()
const router = require('./router')
const cookieParser = require('cookie-parser')
const session = require('express-session')
const flash = require('connect-flash')
const dotenv = require('dotenv')
const csrf = require('csurf')
dotenv.config()
app.use(express.urlencoded({extended: false}))
app.use(express.json())
app.use(express.static('public'))
app.use(cookieParser('secret'))
app.use(session({
secret: 'secret',
cookie: {maxAge: null},
resave: false,
saveUninitialized: false
}))
app.use(flash())
app.set('views', 'views')
app.set('view engine', 'ejs')
app.use(csrf())
app.use(function(req, res, next) {
res.locals.csrfToken = req.csrfToken()
next()
})
app.use('/', router)
app.use(function (req, res, next) {
res.status(404).render('404')
})
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).render('404')
})
app.listen(process.env.PORT)
router.js
const express = require('express')
const multer = require('multer')
const multerConfigOpts = require('./multer.config')
const router = express.Router()
const userController = require('./controllers/userController')
const csrf = require('csurf')
var csrfProtection = csrf({ cookie: true })
// set multer configuration options
const upload = multer(multerConfigOpts)
router.get('/', userController.home)
router.get('/about', userController.about)
router.get('/employer', userController.employer)
router.get('/jobSeeker', userController.jobSeeker)
router.get('/ourProcess', userController.process)
router.get('/contact', userController.contactUs)
// Talent Request Post related routes
router.post('/talentrequest',upload.none() ,userController.requestTalent)
// Job Request Post related routs
router.post('/jobrequest', csrfProtection, upload.single('resume'), userController.requestJob)
module.exports = router
Example of my form:
<form action="/jobrequest" method="POST" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button type="submit" class="btn--form-submit">Submit</button>
</div>
</form>
There are more data fields, I just didn't want to bloat the question with unnecessary code. I've been reading that others are having similar issues when using multipart in the form, but I can't seem to figure it out.
I know that my token is being generated inside the form but I'm not sure if its being passed through properly. Any help or pointers would be appreciated. Thank you

So I was able to find a work around solution by adding the following to my form and removing the input hidden field from my form
form action="/talentrequest/*?_csrf=<%= csrfToken %>*" method="POST" enctype="multipart/form-data">
Everything works as it should. Can anyone explain the potential risks involved with this?

Related

What is the reason req.body is displayed as undefined?

Hi i am making a blog with node js. And now I am implementing the function to write a post, but there is a problem in this process.
new.ejs is a screen for creating a post.
new.ejs
<div>
<h2> new post </h2>
<form action="/articles" method = "POST">
<h4>title</h4>
<input required type="text" name = "title" / class='form-control'><br>
<h4>description</h4>
<textarea name="description" class = 'form-control'></textarea><br>
<h4>contents</h4>
<textarea id="mytextarea" name="contents" rows = '10'></textarea>
취소<button type = "submit" class="btn btn-primary">저장</button>
</form>
</div>
and
article.js
var express = require("express");
var app = express();
var router = express.Router();
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));
router.get("/", function (req, res) {
res.send("article");
});
router.get("/new/home", function (req, res) {
res.render("articles/index");
});
router.get("/new", function (req, res) {
res.render("articles/new");
});
router.post("/", function (req, res) {
console.log(req.body.title);
});
module.exports = router;
Here, when req.body.title is executed, req.body becomes undefined. And in the code editor window, a sentence stating that bordyParser has been deprecated appears as the middle line is drawn. How to solve it
The EJS file is POSTing to /articles, but your POST route in the NodeJS is /.
To fix the 'body-parser is deprecated' messages, change the code as follows :-
...
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));
...
to
...
app.use(express.urlencoded());
...
Body Parser is now part of Express itself.

Cannot read property of undefined / req.body returns undefined

I'm trying to do the most basic POST request in express but my req.body keeps returning undefined, I've googled similar issues but I can't find the solution that would work for me.
The form in HTML:
<form method="POST" class="vote">
<input type="text" name="test">
<button type="submit">TEST VOTE</button>
</form>
and in my post.js file
const express = require('express');
const app = express();
app.use(express.urlencoded({
extended: true
}));
app.post('/test', function (req, res) {
console.log('post to /test');
var data = req.body.test;
console.log(data);
What am I doing wrong here?
Try using body-parser node module which will parse the post data and append it on request object so that you can access it.
body-parser extract the entire body portion of an incoming request stream and exposes it on request.body.
const express = require('express');
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/test', function (req, res) {
console.log('post to /test');
var data = req.body;
console.log(data);
//rest of the code
}
Actually you didn't set the "action" field in your "form" tag.
You have to set your action field to
action= "/test"

POST not working while using express + bootstrap modal

I made a register form using the bootstrap modal and I can't seem to make the POST request work.
This is the form in my en.pug file
#register.modal.fade(tabindex="-1" role="dialog" aria-labelledby="register form" aria-hidden="true")
.modal-dialog(role="document")
.modal-content
.modal-header
h5#exampleModalLabel.modal-title Sign Up
button.close(type="button" data-dismiss="modal" aria-label="Close")
span(aria-hidden="true") ×
.modal-body
form(action='/register' method='POST' )
.form-group
label.form-control-label(for="name") Name:
input#name.form-control(type="text", placeholder='first and last' name='name')
.form-group
label.form-control-label(for="email") Email:
input#email.form-control(type="email", placeholder='name#email.com', name='email')
.form-group
label.form-control-label(for="password") Password:
input#password.form-control(type="password" name='password')
.form-group
label.form-control-label(for="password") Confirm Password:
input#confirmed-password.form-control(type="password" name='confirmPassword')
hr.mb-4
// /registration form
.modal-footer
button.btn.btn-secondary(type="button" data-dismiss="modal") Close
button.btn.btn-primary(type="submit") Sign Up
This is my server.js file
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
// serve static files from /public
app.use(express.static(__dirname + '/public'));
app.use(cookieParser());
// view engine setup
app.set('view engine', 'pug');
app.set('views', __dirname + '/views');
// include routes
const routes = require('./routes/index');
app.use('/', routes);
app.listen(3000);
And this is the ./routes/index.js
var express = require('express');
const app = express();
var router = express.Router();
router.get('/', (req, res) => {
const cookie = req.cookies.language;
if (cookie) {
res.render('en', { cookie });
} else {
res.render('ro');
}
});
router.get('/en', function (req, res) {
res.cookie('language');
return res.render('en');
});
// GET /
router.get('/ro', function(req, res) {
res.clearCookie('language');
return res.render('ro');
});
app.post("/register", function (req, res) {
console.log('Hellooooooooooooooooo!')
});
module.exports = router;
When I fill out the form and press the Sign Up button I expect to show "Hellooooooooooooooooo!" in the console but nothing happens and I can't seem to figure out why. Am I missing something here?
EDIT: I figured out that I made a mistake in the pug indentation and the submit button was outside the form, reason for which completing the form and pressing the sign up button didn't do anything
You have to use bodyparser middleware.
We had to install body-parser before express version 4 update, but express now supports body-parser by default.
Just add
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Hope it helps.
In the HTML, in the input tags of the form of the bootstrap modal window, I used the name attribut (instead of the id attribute) and it is working.
//install body-parser npm and require it
const bodyParser = require('body-parser')
//write code below of app.use(cookieParser()) in server.js
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
router.post('/', function(req, res){
console.log(req.body) //here you will get POST data
})

Invalid Token using expressjs csurf middleware example

I've tried using the expressjs csurf example from https://github.com/expressjs/csurf 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 /process'. The token is being passed to the form on the GET. Any ideas?
Is 'app.use(csrfProtection)' not working? (used in the non working example, if I remove the 'use(csrfP..' and use the methodology from the working example to use the csrf module, IE, passing 'csrfProtection' to the 'get' and 'post' methods, the second example works)
Works:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
// create express app
var app = express()
app.set('view engine', 'ejs')
// parse cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
app.get('/form', csrfProtection, function(req, res) {
// pass the csrfToken to the view
var tkn = req.csrfToken()
console.log(tkn)
res.render('index', { csrfToken: tkn })
})
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed')
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
html/ejs:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
Favorite color: <input type="text" name="favoriteColor">
<button type="submit">Submit</button>
</form>
</body>
</html>
Does not work:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
// create express app
var app = express()
app.set('view engine', 'ejs')
// parse cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
// create api router
var api = createApiRouter()
// mount api before csrf is appended to the app stack
app.use('/api', api)
// now add csrf, after the "/api" was mounted
app.use(csrfProtection)
app.get('/form', function(req, res) {
// pass the csrfToken to the view
var tkn = req.csrfToken()
console.log(tkn)
res.render('index', { csrfToken: tkn })
})
app.post('/process', parseForm, function(req, res) {
res.send('csrf was required to get here')
})
function createApiRouter() {
var router = new express.Router()
router.post('/getProfile', function(req, res) {
res.send('no csrf to get here')
})
return router
}
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app2 listening at http://%s:%s", host, port)
})
In your second example, you are not passing the csrfProtection middleware to the POST processing chain. It should be
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('csrf was required to get here')
})

Express CSRF token validation

I'm having issues with CSRF tokens. When I submit a form, a new XSRF-TOKEN is being generated but I think I'm generating two different tokens, I'm kinda confused. There's also a token called _csrf, so I see two different cookies in developer tools (XSRF-TOKEN and _csrf), _csrf doesn't change after a post.
What I want to do is to generate a new token for each post request and check whether it's valid or not. One thing I know that I should do it for security, but I stuck.
It has been a long day and I'm new into Express and NodeJS.
Here's my current setup.
var express = require('express')
, passport = require('passport')
, flash = require('connect-flash')
, utils = require('./utils')
, csrf = require('csurf')
// setup route middlewares
,csrfProtection = csrf({ cookie: true })
, methodOverride = require('method-override')
, bodyParser = require("body-parser")
, parseForm = bodyParser.urlencoded({ extended: false })
, cookieParser = require('cookie-parser')
, cookieSession = require('cookie-session')
, LocalStrategy = require('passport-local').Strategy
, RememberMeStrategy = require('../..').Strategy;
var app = express();
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.logger());
app.use(express.static(__dirname + '/../../public'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(express.session({ secret: 'keyboard cat' }));
app.use(flash());
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.authenticate('remember-me'));
app.use(app.router);
app.use(csrf());
app.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
res.locals.csrftoken = req.csrfToken();
next();
});
Routes
app.get('/form', csrfProtection, function(req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken()});
});
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed');
});
send.ejs (/form GET)
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
Favorite color: <input type="text" name="favoriteColor">
<button type="submit">Submit</button>
</form>
Based on the amount of code you shared, I will mention a few things that don't look quite right to me:
1 . You may need to swap the lines below so that csrf runs before the routes.
app.use(csrf());
app.use(app.router);
2 . The csrftoken setup needs to also be placed before the routes.
app.use(csrf());
app.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
res.locals.csrftoken = req.csrfToken();
next();
});
app.use(app.router);
3 . You'll need to use locals.csrftoken in your form:
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="<%= csrftoken %>">
Favorite color: <input type="text" name="favoriteColor">
<button type="submit">Submit</button>
</form>
the token in the cookie will be completely different than the one in the express session. you want to check for one or the other not both.
i would disable the cookies entirely! as it worked for me.
var csrfProtection = csurf({ cookie: false });
the author mentions it here
https://github.com/expressjs/csurf/issues/52
next you want to the "X-CSRF-Token" to the header on ajax post found here:
Express.js csrf token with jQuery Ajax
Below code is working for me. Let me know in case you still face issue.
As mentioned that you wish to use cookies, you have make csurf aware that you are using cookies for setting the CSRF token.
Step1: Configuration
var csrf = require('csurf');
var cookieparser= require('cookie-parser');
//cookieparser must be placed before csrf
app.use(bodyparser.urlencoded({extended:false}));
app.use(cookieParser('randomStringisHere222'));
app.use(csrf({cookie:{key:XSRF-TOKEN,path:'/'}}));
//add the your app routes here
app.use("/api", person);
app.use("/", home);
Step2: In the route,
res.render('myViewPage',{csrfTokenFromServer:req.csrfToken()});
Step3: Include a hidden field in the HTML for csrf token Example:
<form action="/api/person" method="POST">
<input type="hidden" name="_csrf" value=<%=csrfTokenFromServer %> />
First name:<br>
<input type="text" name="firstname" value="">
<br>
Last name:<br>
<input type="text" name="lastname" value="">
<br><br>
<input type="submit" value="Submit">
</form>
When we set csrf cookie it has default key _csrf. We can override it. So in my case I gave same name to cookie like this.
const csrf = csurf({cookie:{key:'XSRF-TOKEN'}});
app.get('/csrf-token', csrf, (req: Request, res: Response, next: NextFunction) => {
const newToken = req.csrfToken();
res.cookie('XSRF-TOKEN', newToken, {
httpOnly: true,
sameSite: 'strict',
secure: process.env.NODE_ENV === 'production',
});
res.json({ csrfToken: newToken });
});
I dont know if you resolved the issue but its still will help if someone else looking for it.

Resources