Add bouncer to /rsvp/* routes to hinder brute force attacks

This commit is contained in:
2017-02-13 18:37:18 +00:00
parent 7c9ba5744c
commit a6c6e61b0b
4 changed files with 49 additions and 7 deletions

View File

@@ -29,7 +29,6 @@ class RsvpButton extends React.Component {
}
handleRsvpBlur = () => {
this.setState({ inputInvalidMsg: null });
if (this.props.focusCallback) {
this.props.focusCallback(false);
}
@@ -54,6 +53,18 @@ class RsvpButton extends React.Component {
}).then(data => {
if (data.status === 200) {
location.href = data.url;
} else if (data.status === 429) {
this.setState({
inputInvalid: true,
inputInvalidMsg: 'You have made too many incorrect attempts. Please try again later.',
inputDisabled: true,
});
setTimeout(() => {
this.setState({
inputInvalid: false,
inputDisabled: false,
});
}, 10 * 60 * 1000);
} else {
this.setState({
inputInvalid: true,
@@ -111,6 +122,7 @@ class RsvpButton extends React.Component {
onFocus={this.handleRsvpFocus}
onBlur={this.handleRsvpBlur}
onChange={this.handleRsvpChange}
disabled={this.state.inputDisabled}
id="code"
type="text"
name="code"

View File

@@ -10,6 +10,7 @@ import React from 'react';
import ReactDOM from 'react-dom/server';
import UniversalRouter from 'universal-router';
import PrettyError from 'pretty-error';
import Bouncer from 'express-bouncer';
import App from './components/App';
import Html from './components/Html';
import { ErrorPageWithoutStyle } from './routes/error/ErrorPage';
@@ -28,6 +29,17 @@ import { sendSlackMsgWithDebounce } from './core/slack';
const app = express();
Rollbar.init();
const bouncer = Bouncer(10 * 60 * 1000, 60 * 60 * 1000, 5);
bouncer.blocked = (req, res, next, remaining) => {
let time = Math.ceil(remaining / 1000);
let suffix = ' seconds';
if (time > 120) {
time = Math.ceil(time / 60);
suffix = ' minutes';
}
const body = `You have made too many incorrect attempts. Please wait ${time} ${suffix}.`;
res.status(429).send(body);
};
// Tell any CSS tooling (such as Material UI) to use all vendor prefixes if the
// user agent is not known.
@@ -84,15 +96,15 @@ app.use('/graphql', expressGraphQL(req => ({
// -----------------------------------------------------------------------------
//
app.get('/:password', (req, res, next) => {
if (req.params.password.length !== 4) {
app.get('/:code', (req, res, next) => {
if (req.params.code.length !== 4) {
// Not a password
next();
} else {
Person.findAll({ where: { password: req.params.password } })
Person.findAll({ where: { password: req.params.code } })
.then(data => {
if (data.length > 0) {
res.redirect(`/rsvp/${req.params.password}`);
res.redirect(`/rsvp/${req.params.code}`);
} else {
next();
}
@@ -107,6 +119,19 @@ app.post('/rsvp', (req, res) => {
res.redirect(`/rsvp/${req.body.code}`);
});
app.get('/rsvp/:code', bouncer.block, (req, res, next) => {
Person.findAll({ where: { password: req.params.code } })
.then(data => {
if (data.length > 0) {
bouncer.reset(req);
}
next();
}).catch((err) => {
Rollbar.handleError(err);
next(err);
});
});
app.post('/rsvp/save', (req, res) => {
try {
const update = {};