Add simple CSRF protection for the admin API

This commit is contained in:
Jonas Lochmann 2020-01-13 00:00:00 +00:00
parent e636fe0d8e
commit 93e441e821
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36

View file

@ -18,11 +18,11 @@
import { urlencoded } from 'body-parser'
import * as escape from 'escape-html'
import { Router } from 'express'
import { BadRequest, Conflict } from 'http-errors'
import { BadRequest, Conflict, Forbidden } from 'http-errors'
import { Database } from '../database'
import { addPurchase } from '../function/purchase'
import { getStatusMessage, setStatusMessage } from '../function/statusmessage'
import { generatePurchaseId } from '../util/token'
import { generateAuthToken, generatePurchaseId } from '../util/token'
import { WebsocketApi } from '../websocket'
export const createAdminRouter = ({ database, websocket }: {
@ -31,6 +31,8 @@ export const createAdminRouter = ({ database, websocket }: {
}) => {
const router = Router()
const serverToken = generateAuthToken()
router.get('/', (_, res) => {
res.send('<html><body><a href="/admin/status">status</a><br><a href="/admin/status-message">Status message</a><br><a href="/admin/unlock-premium">unlock premium</a></body></html>')
})
@ -45,7 +47,7 @@ export const createAdminRouter = ({ database, websocket }: {
try {
const currentStatusMessage = await getStatusMessage({ database })
res.send('<html><body><form action="/admin/status-message" method="post"><textarea name="smessage" rows="20" cols="100">' + escape(currentStatusMessage) + '</textarea><input type="submit" value="Save"></form></body></html>')
res.send('<html><body><form action="/admin/status-message" method="post"><textarea name="smessage" rows="20" cols="100">' + escape(currentStatusMessage) + '</textarea><input type="submit" value="Save"><input type="hidden" name="token" value="' + escape(serverToken) + '"></form></body></html>')
} catch (ex) {
next(ex)
}
@ -53,10 +55,14 @@ export const createAdminRouter = ({ database, websocket }: {
router.post('/status-message', urlencoded({ extended: false }), async (req, res, next) => {
try {
if (typeof req.body !== 'object' || typeof req.body.smessage !== 'string') {
if (typeof req.body !== 'object' || typeof req.body.smessage !== 'string' || typeof req.body.token !== 'string') {
throw new BadRequest()
}
if (req.body.token !== serverToken) {
throw new Forbidden()
}
const newStatusMessage = req.body.smessage as string
await setStatusMessage({ database, newStatusMessage })
@ -70,15 +76,19 @@ export const createAdminRouter = ({ database, websocket }: {
})
router.get('/unlock-premium', (_, res) => (
res.send('<html><body><form action="/admin/unlock-premium" method="post">mail: <input name="mail" /><br><input type="radio" name="duration" value="month" />Month<br /><input type="radio" name="duration" value="year" />Year<br><input type="submit" value="Unlock"></form></body></html>')
res.send('<html><body><form action="/admin/unlock-premium" method="post">mail: <input name="mail" /><br><input type="radio" name="duration" value="month" />Month<br /><input type="radio" name="duration" value="year" />Year<br><input type="submit" value="Unlock"><input type="hidden" name="token" value="' + escape(serverToken) + '"></form></body></html>')
))
router.post('/unlock-premium', urlencoded({ extended: false }), async (req, res, next) => {
try {
if (typeof req.body !== 'object' || typeof req.body.mail !== 'string' || typeof req.body.duration !== 'string') {
if (typeof req.body !== 'object' || typeof req.body.mail !== 'string' || typeof req.body.duration !== 'string' || typeof req.body.token !== 'string') {
throw new BadRequest()
}
if (req.body.token !== serverToken) {
throw new Forbidden()
}
const mail: string = req.body.mail
const type: string = req.body.duration