Skip to content

Commit

Permalink
CSRF protection.
Browse files Browse the repository at this point in the history
  • Loading branch information
phulin committed Oct 3, 2019
1 parent 088a8ed commit 61e72a9
Show file tree
Hide file tree
Showing 29 changed files with 131 additions and 17 deletions.
1 change: 1 addition & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const path = require('path');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const expressValidator = require('express-validator');
const csurf = require('csurf');
const flash = require('connect-flash');
const session = require('express-session');
const passport = require('passport');
Expand Down
48 changes: 42 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"connect-flash": "^0.1.1",
"connect-mongodb-session": "^2.2.0",
"core-js": "^2.6.9",
"csurf": "^1.10.0",
"express": "^4.17.1",
"express-fileupload": "^1.1.5",
"express-messages": "^1.0.1",
Expand Down
7 changes: 6 additions & 1 deletion routes/cube_routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const fs = require('fs');
const fetch = require('node-fetch');
const rp = require('request-promise');
const cheerio = require('cheerio');
const csurf = require('csurf');

var {
addAutocard,
generatePack,
Expand Down Expand Up @@ -46,7 +48,8 @@ let Draft = require('../models/draft');
let CardRating = require('../models/cardrating');

const {
ensureAuth
ensureAuth,
csrfProtection,
} = require('./middleware');

var token = null;
Expand Down Expand Up @@ -202,6 +205,8 @@ function abbreviate(name) {
return name.length < 20 ? name : name.slice(0, 20) + '…';
}

router.use(csrfProtection);

// Add Submit POST Route
router.post('/add', ensureAuth, async (req, res) => {
if (req.body.name.length < 5) {
Expand Down
5 changes: 4 additions & 1 deletion routes/dev_routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const mailer = require("nodemailer");
const fs = require('fs')

const {
ensureAuth
ensureAuth,
csrfProtection
} = require('./middleware');

// Bring in models
Expand All @@ -16,6 +17,8 @@ let Blog = require('../models/blog')

var adminname = 'Dekkaru';

router.use(csrfProtection);

router.get('/blog', function(req, res) {
res.redirect('/dev/blog/0');
});
Expand Down
10 changes: 9 additions & 1 deletion routes/middleware.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const csurf = require('csurf');

const ensureAuth = (req, res, next) => {
if (req.isAuthenticated()) {
return next();
Expand All @@ -7,6 +9,12 @@ const ensureAuth = (req, res, next) => {
}
}

const csrfProtection = [csurf(), (req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
}];

module.exports = {
ensureAuth
ensureAuth,
csrfProtection,
}
5 changes: 4 additions & 1 deletion routes/users_routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ let Cube = require('../models/cube')
let Deck = require('../models/deck')

const {
ensureAuth
ensureAuth,
csrfProtection,
} = require('./middleware');

// For consistency between different forms, validate username through this function.
Expand All @@ -31,6 +32,8 @@ function checkUsernameValid(req) {
return req;
}

router.use(csrfProtection);

//Lost password form
router.get('/lostpassword', function(req, res) {
res.render('user/lostpassword');
Expand Down
4 changes: 3 additions & 1 deletion src/components/CardModalForm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { Component } from 'react';

import { csrfFetch } from '../util/CSRF';

import CardModal from './CardModal';
import CardModalContext from './CardModalContext';

Expand Down Expand Up @@ -115,7 +117,7 @@ class CardModalForm extends Component {
return;
}

let response = await fetch('/cube/api/updatecard/' + document.getElementById('cubeID').value, {
let response = await csrfFetch('/cube/api/updatecard/' + document.getElementById('cubeID').value, {
method: 'POST',
body: JSON.stringify({ src: card, updated }),
headers: {
Expand Down
6 changes: 4 additions & 2 deletions src/components/EditCollapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { Component } from 'react';

import { Button, Card, CardHeader, Col, Collapse, Container, Form, FormGroup, Input, Label, Row, UncontrolledAlert } from 'reactstrap';

import { CSRFForm } from '../util/CSRF';

import ContentEditable from './ContentEditable';

function clickToolbar(event) {
Expand Down Expand Up @@ -133,7 +135,7 @@ class EditCollapse extends Component {
</Col>
</Row>
<div className="collapse editForm">
<Form id="changelistForm" method="POST" action={`/cube/edit/${cubeID}`}>
<CSRFForm id="changelistForm" method="POST" action={`/cube/edit/${cubeID}`}>
<Row>
<Col>
<Label>Changelist:</Label>
Expand Down Expand Up @@ -181,7 +183,7 @@ class EditCollapse extends Component {
<Button color="danger" onClick={discardAll}>Discard All</Button>
</Col>
</Row>
</Form>
</CSRFForm>
</div>
</Container>
</Collapse>
Expand Down
3 changes: 2 additions & 1 deletion src/components/GroupModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
UncontrolledAlert,
} from 'reactstrap';

import { csrfFetch } from '../util/CSRF';
import { fromEntries } from '../util/Util';

import AutocardListItem from './AutocardListItem';
Expand Down Expand Up @@ -143,7 +144,7 @@ class GroupModal extends Component {
tags: tags || undefined,
addTags, deleteTags,
};
const response = await fetch(`/cube/api/updatecards/${cubeID}`, {
const response = await csrfFetch(`/cube/api/updatecards/${cubeID}`, {
method: 'POST',
body: JSON.stringify({ selected, updated }),
headers: {
Expand Down
3 changes: 2 additions & 1 deletion src/components/ListView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Component } from 'react';

import { Input } from 'reactstrap';

import { csrfFetch } from '../util/CSRF';
import { arraysEqual, fromEntries } from '../util/Util';

import DisplayContext from './DisplayContext';
Expand Down Expand Up @@ -94,7 +95,7 @@ class ListViewRaw extends Component {
return;
}

fetch(`/cube/api/updatecard/${cubeID}`, {
csrfFetch(`/cube/api/updatecard/${cubeID}`, {
method: 'POST',
body: JSON.stringify({
src: card,
Expand Down
4 changes: 3 additions & 1 deletion src/components/SortCollapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { Component } from 'react';

import { Button, Col, Collapse, Container, Input, Row, UncontrolledAlert } from 'reactstrap';

import { csrfFetch } from '../util/CSRF';

import SortContext from './SortContext';

class SortCollapseRaw extends Component {
Expand All @@ -24,7 +26,7 @@ class SortCollapseRaw extends Component {
handleSave() {
const { primary, secondary } = this.props;

fetch("/cube/api/savesorts/" + $('#cubeID').val(), {
csrfFetch("/cube/api/savesorts/" + $('#cubeID').val(), {
method: "POST",
body: JSON.stringify({
sorts: [primary, secondary],
Expand Down
4 changes: 3 additions & 1 deletion src/cube_playtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import ReactDOM from 'react-dom';

import { Button, Card, CardBody, CardFooter, CardHeader, CardTitle, Col, FormGroup, Input, Label, Row, UncontrolledAlert, UncontrolledCollapse } from 'reactstrap';

import { csrfFetch } from './util/CSRF';

import DynamicFlash from './components/DynamicFlash';

const range = (lo, hi) => Array.from(Array(hi - lo).keys()).map(n => n + lo);
Expand Down Expand Up @@ -187,7 +189,7 @@ class CubePlaytest extends Component {

deleteFormat(cube, formatID) {
console.log(formatID);
fetch(`/cube/format/remove/${cube};${formatID}`, {
csrfFetch(`/cube/format/remove/${cube};${formatID}`, {
method: 'DELETE',
}).then(response => {
this.addAlert({
Expand Down
25 changes: 25 additions & 0 deletions src/util/CSRF.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { Form, Input } from 'reactstrap';

const getToken = () => {
const meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.getAttribute('content') : null;
}

export const csrfFetch = (resource, init) => {
init.credentials = init.credentials || 'same-origin';
init.headers = {
...init.headers,
'CSRF-Token': getToken(),
}
return fetch(resource, init);
}

export const CSRFForm = ({ children, ...props }) =>
<Form {...props}>
<Input type="hidden" name="_csrf" value={getToken()} />
{children}
</Form>;

export default { csrfFetch };
1 change: 1 addition & 0 deletions views/blog/devblog.pug
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ block content
.card(style='padding:10px')
h5 Create New Blog Post
form(method='POST', action='/dev/blogpost/')
input(type='hidden', name='_csrf', value=csrfToken)
#form-group
label Title:
input.form-control(maxlength='200' name='title', type='text')
Expand Down
1 change: 1 addition & 0 deletions views/cube/bulk_upload.pug
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ block content
p.editlist#changelist
.row
form#changelistForm(method='POST', action='/cube/edit/'+cube_id)
input(type='hidden', name='_csrf', value=csrfToken)
input(id='addedcardshidden', type='hidden', value=added)
input(id='titlehidden', type='hidden', name='title', value='Cube Bulk Import - Automatic Post')
input#changelistFormBody(type='hidden', name='body')
Expand Down
1 change: 1 addition & 0 deletions views/cube/cube_blog.pug
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ block cube_content
.modal-dialog.modal-lg(role='document')
.modal-content
form#postBlogForm(method='POST', action='/cube/blog/post/'+cube_id)
input(type='hidden', name='_csrf', value=csrfToken)
.modal-header
h5#blogEditTitle Edit Blog Post
button.close(type='button', data-dismiss='modal', aria-label='Close')
Expand Down
1 change: 1 addition & 0 deletions views/cube/cube_deckbuilder.pug
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ block cube_toolbar
link(rel='stylesheet' href='/css/draft.css')
input#basicsraw(type='hidden', name='basicsraw', value=basics_raw)
form#submitDeckForm(method='POST', action='/cube/editdeck/'+deckid)
input(type='hidden', name='_csrf', value=csrfToken)
input#deckraw(type='hidden', name='draftraw', value=deck_raw)

#dragelement(style='position:absolute;z-index: 9999;')
Expand Down
Loading

0 comments on commit 61e72a9

Please sign in to comment.