CSRF/XSS protection (#374)
* /api endpoints only allows requests with application/json Content-Type Otherwise sends 406 Unacceptable * Uses CSRF token The CSRF token is sent as the cookie 'XSRF-TOKEN' on all HTML page requests. This token is picked up automatically by axios and sent to the API with all requests as an 'X-XSRF-TOKEN' header. The middleware runs on all routes and verifies that the token matches what's stored in the session.
This commit is contained in:
parent
4476405021
commit
6cbc376d6e
3 changed files with 34 additions and 7 deletions
|
@ -71,6 +71,7 @@
|
|||
"cookie-parser": "^1.4.1",
|
||||
"cors": "^2.8.1",
|
||||
"csslint": "^0.10.0",
|
||||
"csurf": "^1.9.0",
|
||||
"decomment": "^0.8.7",
|
||||
"dotenv": "^2.0.0",
|
||||
"dropzone": "^4.3.0",
|
||||
|
|
|
@ -7,6 +7,7 @@ import session from 'express-session';
|
|||
import connectMongo from 'connect-mongo';
|
||||
import passport from 'passport';
|
||||
import path from 'path';
|
||||
import csurf from 'csurf';
|
||||
|
||||
// Webpack Requirements
|
||||
import webpack from 'webpack';
|
||||
|
@ -23,6 +24,7 @@ import files from './routes/file.routes';
|
|||
import aws from './routes/aws.routes';
|
||||
import serverRoutes from './routes/server.routes';
|
||||
import embedRoutes from './routes/embed.routes';
|
||||
import { requestsOfTypeJSON } from './utils/requestsOfType';
|
||||
|
||||
import { renderIndex } from './views/index';
|
||||
import { get404Sketch } from './views/404Page';
|
||||
|
@ -73,18 +75,27 @@ app.use(session({
|
|||
autoReconnect: true
|
||||
})
|
||||
}));
|
||||
|
||||
// Enables CSRF protection and stores secret in session
|
||||
app.use(csurf());
|
||||
// Middleware to add CSRF token as cookie to some requests
|
||||
const csrfToken = (req, res, next) => {
|
||||
res.cookie('XSRF-TOKEN', req.csrfToken());
|
||||
next();
|
||||
};
|
||||
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
app.use('/api', users);
|
||||
app.use('/api', sessions);
|
||||
app.use('/api', projects);
|
||||
app.use('/api', files);
|
||||
app.use('/api', aws);
|
||||
app.use('/api', requestsOfTypeJSON(), users);
|
||||
app.use('/api', requestsOfTypeJSON(), sessions);
|
||||
app.use('/api', requestsOfTypeJSON(), projects);
|
||||
app.use('/api', requestsOfTypeJSON(), files);
|
||||
app.use('/api', requestsOfTypeJSON(), aws);
|
||||
// this is supposed to be TEMPORARY -- until i figure out
|
||||
// isomorphic rendering
|
||||
app.use('/', serverRoutes);
|
||||
app.use('/', csrfToken, serverRoutes);
|
||||
|
||||
app.use('/', embedRoutes);
|
||||
app.use('/', csrfToken, embedRoutes);
|
||||
app.get('/auth/github', passport.authenticate('github'));
|
||||
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (req, res) => {
|
||||
res.redirect('/');
|
||||
|
|
15
server/utils/requestsOfType.js
Normal file
15
server/utils/requestsOfType.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
express middleware that sends a 406 Unacceptable
|
||||
response if an incoming request's Content-Type
|
||||
header does not match `type`
|
||||
*/
|
||||
const requestsOfType = type => (req, res, next) => {
|
||||
if (req.get('content-type') != null && !req.is(type)) {
|
||||
return next({ statusCode: 406 }); // 406 UNACCEPTABLE
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
export default requestsOfType;
|
||||
export const requestsOfTypeJSON = () => requestsOfType('application/json');
|
Loading…
Reference in a new issue