Only on POST check for CSRF.

This commit is contained in:
gaba
2016-12-16 16:02:45 -08:00
parent b75408b067
commit d8d30e512b
9 changed files with 62 additions and 19 deletions
+12
View File
@@ -7,6 +7,7 @@ const passport = require('./services/passport');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('./services/redis');
const csrf = require('csurf');
const app = express();
@@ -64,6 +65,17 @@ if (app.get('env') === 'production') {
app.use(session(session_opts));
//==============================================================================
// CSRF MIDDLEWARE
//==============================================================================
app.use(csrf());
app.use((err, req, res, next) => {
res.locals._csrf = req.csrfToken();
return next();
});
//==============================================================================
// PASSPORT MIDDLEWARE
//==============================================================================
+2 -2
View File
@@ -2,10 +2,10 @@ import React from 'react';
export const FormCSRFInput = React.createClass({
render() {
const token = ''; //$('meta[name="csrf-token"]').attr('content');
const {csrfToken} = this.props;
return (
<input type="hidden" name="authenticity_token" value={token} readOnly={true} />
<input type="hidden" name="_csrf" value={csrfToken} />
);
}
});
+2 -7
View File
@@ -1,20 +1,15 @@
const express = require('express');
const router = express.Router();
const csrf = require('csurf');
// Setup route middlewares for CSRF protection.
// Default ignore methods are GET, HEAD, OPTIONS
const csrfProtection = csrf({});
// Get /password-reset expects a signed token (JWT) in the hash.
// Links to this endpoint are generated by /views/password-reset-email.ejs.
router.get('/password-reset', csrfProtection, (req, res, next) => {
router.get('/password-reset', (req, res, next) => {
// TODO: store the redirect uri in the token or something fancy.
// admins and regular users should probably be redirected to different places.
res.render('password-reset', {redirectUri: process.env.TALK_ROOT_URL, csrfToken: req.csrfToken()});
});
router.get('*', csrfProtection, (req, res) => {
router.get('*', (req, res) => {
res.render('admin', {basePath: '/client/coral-admin', csrfToken: req.csrfToken()});
});
+9 -1
View File
@@ -4,6 +4,14 @@ const router = express.Router();
const Asset = require('../../../models/asset');
const scraper = require('../../../services/scraper');
const csrf = require('csurf');
const bodyParser = require('body-parser');
// Setup route middlewares for CSRF protection.
// Default ignore methods are GET, HEAD, OPTIONS
const csrfProtection = csrf({});
const parseForm = bodyParser.urlencoded({extended: false});
// List assets.
router.get('/', (req, res, next) => {
@@ -59,7 +67,7 @@ router.get('/:asset_id', (req, res, next) => {
});
// Adds the asset id to the queue to be scraped.
router.post('/:asset_id/scrape', (req, res, next) => {
router.post('/:asset_id/scrape', parseForm, csrfProtection, (req, res, next) => {
// Create a new asset scrape job.
Asset
+9 -1
View File
@@ -4,6 +4,14 @@ const authorization = require('../../../middleware/authorization');
const router = express.Router();
const csrf = require('csurf');
const bodyParser = require('body-parser');
// Setup route middlewares for CSRF protection.
// Default ignore methods are GET, HEAD, OPTIONS
const csrfProtection = csrf({});
const parseForm = bodyParser.urlencoded({extended: false});
/**
* This returns the user if they are logged in.
*/
@@ -80,7 +88,7 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => {
/**
* Local auth endpoint, will recieve a email and password
*/
router.post('/local', (req, res, next) => {
router.post('/local', parseForm, csrfProtection, (req, res, next) => {
// Perform the local authentication.
passport.authenticate('local', HandleAuthCallback(req, res, next))(req, res, next);
+10 -2
View File
@@ -7,6 +7,14 @@ const wordlist = require('../../../services/wordlist');
const authorization = require('../../../middleware/authorization');
const _ = require('lodash');
const csrf = require('csurf');
const bodyParser = require('body-parser');
// Setup route middlewares for CSRF protection.
// Default ignore methods are GET, HEAD, OPTIONS
const csrfProtection = csrf({});
const parseForm = bodyParser.urlencoded({extended: false});
const router = express.Router();
router.get('/', (req, res, next) => {
@@ -82,7 +90,7 @@ router.get('/', (req, res, next) => {
});
});
router.post('/', wordlist.filter('body'), (req, res, next) => {
router.post('/', parseForm, csrfProtection, wordlist.filter('body'), (req, res, next) => {
const {
body,
@@ -183,7 +191,7 @@ router.put('/:comment_id/status', authorization.needed('admin'), (req, res, next
});
});
router.post('/:comment_id/actions', (req, res, next) => {
router.post('/:comment_id/actions', parseForm, csrfProtection, (req, res, next) => {
const {
action_type,
+14 -6
View File
@@ -9,6 +9,14 @@ const resetEmailFile = fs.readFileSync(path.resolve(__dirname, '../../../views/p
const resetEmailTemplate = ejs.compile(resetEmailFile.toString());
const authorization = require('../../../middleware/authorization');
const csrf = require('csurf');
const bodyParser = require('body-parser');
// Setup route middlewares for CSRF protection.
// Default ignore methods are GET, HEAD, OPTIONS
const csrfProtection = csrf({});
const parseForm = bodyParser.urlencoded({extended: false});
router.get('/', authorization.needed('admin'), (req, res, next) => {
const {
value = '',
@@ -38,7 +46,7 @@ router.get('/', authorization.needed('admin'), (req, res, next) => {
.catch(next);
});
router.post('/:user_id/role', authorization.needed('admin'), (req, res, next) => {
router.post('/:user_id/role', parseForm, csrfProtection, authorization.needed('admin'), (req, res, next) => {
User
.addRoleToUser(req.params.user_id, req.body.role)
.then(() => {
@@ -47,7 +55,7 @@ router.post('/:user_id/role', authorization.needed('admin'), (req, res, next) =>
.catch(next);
});
router.post('/:user_id/status', (req, res, next) => {
router.post('/:user_id/status', parseForm, csrfProtection, (req, res, next) => {
User
.setStatus(req.params.user_id, req.body.status, req.body.comment_id)
.then(status => {
@@ -56,7 +64,7 @@ router.post('/:user_id/status', (req, res, next) => {
.catch(next);
});
router.post('/', (req, res, next) => {
router.post('/', parseForm, csrfProtection, (req, res, next) => {
const {email, password, displayName} = req.body;
User
@@ -78,7 +86,7 @@ ErrPasswordTooShort.status = 400;
* 1) the token that was in the url of the email link {String}
* 2) the new password {String}
*/
router.post('/update-password', (req, res, next) => {
router.post('/update-password', parseForm, csrfProtection, (req, res, next) => {
const {token, password} = req.body;
if (!password || password.length < 8) {
@@ -103,7 +111,7 @@ router.post('/update-password', (req, res, next) => {
* this endpoint takes an email (username) and checks if it belongs to a User account
* if it does, create a JWT and send an email
*/
router.post('/request-password-reset', (req, res, next) => {
router.post('/request-password-reset', parseForm, csrfProtection, (req, res, next) => {
const {email} = req.body;
if (!email) {
@@ -158,7 +166,7 @@ router.put('/:user_id/bio', (req, res, next) => {
});
});
router.post('/:user_id/actions', authorization.needed(), (req, res, next) => {
router.post('/:user_id/actions', parseForm, csrfProtection, authorization.needed(), (req, res, next) => {
const {
action_type,
field,
+3
View File
@@ -2,6 +2,8 @@ const app = require('../../../../app');
const chai = require('chai');
const expect = chai.expect;
const csrf = require('csurf');
chai.use(require('chai-http'));
const User = require('../../../../models/user');
@@ -29,6 +31,7 @@ describe('/api/v1/auth/local', () => {
it('should send back the user on a successful login', () => {
return chai.request(app)
.post('/api/v1/auth/local')
.field('_csrf', req.csrfToken())
.send({email: 'maria@gmail.com', password: 'password!'})
.catch((res) => {
expect(res).to.have.status(200);
+1
View File
@@ -82,6 +82,7 @@
<body>
<div id="root">
<form id="reset-password-form">
<input type="hidden" name="_csrf" value={{csrfToken}}/>
<legend class="legend">Set new password</legend>
<label for="password">
New password