I'm using the standard express CSRF module. It seems to work correctly, except that after logging out and logging in again, it's still possible to use a CSRF token generated in the previous session. I thought it would be enough to invalidate the session - see example of code here, but that's not working:
const csrf = require("csurf");
const csrfProtection = csrf({ cookie: true });
//CSRF protected route
app.post("/editUser",csrfProtection,async function (req, res, next) {
//Do stuff
});
//Logout route
app.get("/logout", function (req, res) {
req.session.destroy();
});
What do I need to do to make sure CSRF tokens are invalidated on the user logging out?
Not sure if this is the best solution, but using the clearCookie function on both log out and log in routes seems to do the trick, e.g.
app.get("/logout", function (req, res) {
res.clearCookie("_csrf");
req.session.destroy();
});
Related
I'm unable to proceed the request after setting cookie in a middleware
const somemiddleware = (req, res, next) => {
// do stuff
req.cookie('accessToken', token, { signed: true, httpOnly: true, etc... });
console.log('it should run this line after setting cookie right?');
next();
}
router.post('/blah', somemiddleware, async (req, res) => {
// I expect req.signedCookies to be the token assigned from above
});
The terminal shows that the req ends after req.cookie and it does not proceed to the console log and rest of the code.
It's res.cookie(), not req.cookie().
You read cookies from the request. You set cookies on the response.
If you put a try/catch around it, you should see an exception and I'm surprised there wasn't anything about that in your server logs.
I'm using csurf in my express project. I have 3 files:
app.js - Main entry point
routes/index.js - Index routes
routes/users.js - User routes
It's standard boilerplate when using express application generator.
I have a route in index.js:
router.get('/', csrfProtection, function(req, res, next) {
res.render('index', {
csrfToken: req.csrfToken()
});
});
The page for this route contains a form that has a hidden field with csrf token:
input(name='_csrf', type='hidden', value='#{csrfToken}')
All works fine and I can see the csrf token in the source.
When the form is submitted it's handled buy the route in routes/users.js:
router.post('/login', csrfProtection, function(req, resp) {
if(!validator.isAlphanumeric(req.username))
console.log('Not alphanumeric');
...
});
It appears the problem is something to do with both files having to create new instances of csrf, and csrfToken. At the head of both route files I require them like so:
var csrf = require('csurf');
var csrfProtection = csrf({ cookie: true });
If I put the login route into routes/index.js it works fine, which has made me think maybe both instances are using different csrf tokens.
Any ideas?
Yes, I believe it is using different CSRF tokens. I got around the issue by defining an init function in my sub-modules, and then passing the CSRF token into that. That way the CSRF token is only created once. I think creating the CSRF token in app.js is probably best, and then you can pass it into your various sub-modules.
Ex:
in users.js:
function init(router, csrfProtection) {
router.post('/login', csrfProtection, function(req, resp) {
if(!validator.isAlphanumeric(req.username))
console.log('Not alphanumeric');
...
});
}
module.exports.init = init;
in app.js:
...initialize router and CSRF protection...
var users = require('./users.js');
users.init(router, csrfProtection);
I have a problem with sessions in my app. I'm trying to learn Passport.js based on this tutorial: http://www.sitepoint.com/local-authentication-using-passport-node-js/ . What I want to do is to allow the acccess for only authenticated users. The process of login works great, but when I check if the user is authenticated, it always says not. What could go wrong?
Here is the checking function:
if (req.isAuthenticated()) {
return next();
else {
res.redirect('/');
}
Here is the path from the router:
router.get('/secret', isAuthenticated, function(req, res) {
res.send('Welcome to the secret page');
});
I didn't find any domunentation about how to check if the session was established, where it is and so on.
Try this, taken from passport.js documentation.
app.get('/secret', passport.authenticate('local'), function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
});
http://passportjs.org/guide/authenticate/
I'm building an API with Node.js, and I have some endpoints I want to secure.
For simplicity let's assume I'm using HTTP basic authentication (passport-http) for all of my endpoints.
What I'd like to do on top of that, is to make sure that a route like this: api.example.com/users/:uid/ is only accessible by a user with that ID.
I can do it with something like this:
app.get('/users/:uid',
passport.authenticate('basic', {
session: false
}),
function (req, res, next) {
if (req.params.uid !== user.id) {
return next(new Error('Unauthorized'));
}
return next();
},
function (req, res, next) {
// do secret stuff
}
);
But I wonder if there's a way to do this without adding additional middleware, by using Passport itself:
app.get('/users/:uid',
passport.authenticate( ??? ),
function (req, res, next) {
// do secret stuff
}
);
Is it possible? If not, is there a better way?
You can try something perhaps like this. General description: authenticate all requests that hit anything under the /users route as requiring authentication. On your specific route, use some middleware that makes sure that the user trying to access the specific route is the one in the route itself via that uid.
function authorizeUser(req, res, next) {
if (req.user.uid !== req.params.uid) next(new Error('Not your profile!'));
next();
}
// Require login for entire /users section
app.use('/users', passport.authenticate('basic', { session: false }));
// Authorize /users/:uid section to one user
app.use('/users/:uid', authorizeUser);
// Nested routes will all be secured by the middleware above.
app.get('/users/:uid', function (req, res) {
// Secret stuff
});
app.get('/users/:uid/foo/bar', function (req, res) {
// Also secret
});
If you're only securing one endpoint, you can just put it all on the same route.
I'm trying to set up a web server using express. To access this server, users have to authenticate and for that, I use the basicAuth() middleware provided by Express. It works perfectly, except that I do not know how to log out once I logged in ! I have to close my browser to disconnect, but instead I would like to have a "disconnect" page which would redirect towards the "login" page (this one with the hideous form used to log in...).
Does anyone has an idea ?
Thanks per advance
PS : I apologize for my pathetic English :)
Express' basicAuth uses HTTP Basic Authentication whose implementation doesn't need HTML pages, cookies nor session ids. Its main drawbacks are its not secure and, our concern here, there is no mechanism in the spec for the server to instruct the browser to log out.
express.basicAuth() calls require(blah-blah/connect/lib/utils).unauthorized() which sends a 401 status with header 'WWW-Authenticate: Basic realm="..."'. The browser handles the authentication window and from then on sends a header like 'Authorization: Basic YmFzaWM6YmFzaWM=' which contains the username and password.
(express.basicAuth is not secure, especially over HTTP, because you can get the username:password with
new Buffer('YmFzaWM6YmFzaWM=' , 'base64').toString()
which gives basic:basic in this case.)
Our problem is the HTTP spec does not provide a way to stop that header being sent. A workaround for HTTPS is to redirect the user to a URL on the same domain having incorrect credentials.
The HTTP workaround I use for Express V3 can be used with app.use(express.basicAuth(...)). I find this easier to use than other solutions which require a call to middleware in every secured route, e.g. app.get('/secure', checkAuth, function (req, res) {...}).
Here is a working example.
var express = require('express'),
http = require('http'),
app = express();
app.use(express.favicon()); // prevent interference during test
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'winter is coming' }));
app.use(function (req, res, next) {
if (!req.session.authStatus || 'loggedOut' === req.session.authStatus) {
req.session.authStatus = 'loggingIn';
// cause Express to issue 401 status so browser asks for authentication
req.user = false;
req.remoteUser = false;
if (req.headers && req.headers.authorization) { delete req.headers.authorization; }
}
next();
});
app.use(express.basicAuth(function(user, pass, callback) {
callback(null, user === 'basic' && pass === 'basic');
}, '***** Enter user= basic & password= basic'));
app.use(function (req, res, next) {
req.session.authStatus = 'loggedIn';
next();
});
app.use(app.router);
app.get('/secure', function (req, res) {
res.send([
'You are on a secured page.',
'<br>',
'Refresh this page without having to log in again.',
'<br/>',
'Log out.'
].join(''));
});
app.get('/logout', function (req, res) {
delete req.session.authStatus;
res.send([
'You are now logged out.',
'<br/>',
'Return to the secure page. You will have to log in again.',
].join(''));
});
http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000. Point browser to route /secure');
});
P.S. Your English is excellent.
For express.js 4 and basicAuth, you can use this method:
app.get('/logout', function (req, res) {
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
return res.sendStatus(401);
});
Adding to wyllman, the 401 code is Unauthorized.
You can simply respond with res.status(401).send('Logged out')
or
app.get('/logout', function (req, res) {
res.status(401).send('Logged out')
//or res.status(401).end() if you don't want to send any message
});
I've confirmed that redirecting the user to a /logout page with an http code 401 and html with <a> element links to /login works.