feat: rate-limiting
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
committed by
Philip Molares
parent
e8f4cbabec
commit
876ebad1f3
@@ -29,6 +29,7 @@ module.exports = {
|
||||
allowFraming: true,
|
||||
allowPDFEmbed: true
|
||||
},
|
||||
rateLimitNewNotes: 20,
|
||||
cookiePolicy: 'lax',
|
||||
protocolUseSSL: false,
|
||||
allowAnonymous: true,
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports = {
|
||||
allowFraming: toBooleanConfig(process.env.CMD_CSP_ALLOW_FRAMING),
|
||||
allowPDFEmbed: toBooleanConfig(process.env.CMD_CSP_ALLOW_PDF_EMBED)
|
||||
},
|
||||
rateLimitNewNotes: toIntegerConfig(process.env.CMD_RATE_LIMIT_NEW_NOTES),
|
||||
cookiePolicy: process.env.CMD_COOKIE_POLICY,
|
||||
protocolUseSSL: toBooleanConfig(process.env.CMD_PROTOCOL_USESSL),
|
||||
allowOrigin: toArrayConfig(process.env.CMD_ALLOW_ORIGIN),
|
||||
|
||||
@@ -26,6 +26,9 @@ module.exports = {
|
||||
errorTooLong: function (res) {
|
||||
responseError(res, 413, 'Payload Too Large', 'Shorten your note!')
|
||||
},
|
||||
errorTooManyRequests: function (res) {
|
||||
responseError(res, 429, 'Too Many Requests', 'Try again later.')
|
||||
},
|
||||
errorInternalError: function (res) {
|
||||
responseError(res, 500, 'Internal Error', 'wtf.')
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ const models = require('../../../models')
|
||||
const logger = require('../../../logger')
|
||||
const { urlencodedParser } = require('../../utils')
|
||||
const errors = require('../../../errors')
|
||||
const rateLimit = require('../../middleware/rateLimit')
|
||||
|
||||
const emailAuth = module.exports = Router()
|
||||
|
||||
@@ -37,7 +38,7 @@ passport.use(new LocalStrategy({
|
||||
}))
|
||||
|
||||
if (config.allowEmailRegister) {
|
||||
emailAuth.post('/register', urlencodedParser, function (req, res, next) {
|
||||
emailAuth.post('/register', rateLimit.userEndpoints, urlencodedParser, function (req, res, next) {
|
||||
if (!req.body.email || !req.body.password) return errors.errorBadRequest(res)
|
||||
if (!validator.isEmail(req.body.email)) return errors.errorBadRequest(res)
|
||||
models.User.findOrCreate({
|
||||
@@ -67,7 +68,7 @@ if (config.allowEmailRegister) {
|
||||
})
|
||||
}
|
||||
|
||||
emailAuth.post('/login', urlencodedParser, function (req, res, next) {
|
||||
emailAuth.post('/login', rateLimit.userEndpoints, urlencodedParser, function (req, res, next) {
|
||||
if (!req.body.email || !req.body.password) return errors.errorBadRequest(res)
|
||||
if (!validator.isEmail(req.body.email)) return errors.errorBadRequest(res)
|
||||
passport.authenticate('local', {
|
||||
|
||||
33
lib/web/middleware/rateLimit.js
Normal file
33
lib/web/middleware/rateLimit.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict'
|
||||
|
||||
const { rateLimit } = require('express-rate-limit')
|
||||
const errors = require('../../errors')
|
||||
const config = require('../../config')
|
||||
|
||||
const determineKey = (req) => {
|
||||
if (req.user) {
|
||||
return req.user.id
|
||||
}
|
||||
return req.header('cf-connecting-ip') || req.ip
|
||||
}
|
||||
|
||||
// limits requests to user endpoints (login, signup) to 10 requests per 5 minutes
|
||||
const userEndpoints = rateLimit({
|
||||
windowMs: 5 * 60 * 1000,
|
||||
limit: 10,
|
||||
keyGenerator: determineKey,
|
||||
handler: (req, res) => errors.errorTooManyRequests(res)
|
||||
})
|
||||
|
||||
// limits the amount of requests to the new note endpoint per 5 minutes based on configuration
|
||||
const newNotes = rateLimit({
|
||||
windowMs: 5 * 60 * 1000,
|
||||
limit: config.rateLimitNewNotes,
|
||||
keyGenerator: determineKey,
|
||||
handler: (req, res) => errors.errorTooManyRequests(res)
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
userEndpoints,
|
||||
newNotes
|
||||
}
|
||||
@@ -7,13 +7,22 @@ const router = module.exports = Router()
|
||||
|
||||
const noteController = require('./controller')
|
||||
const slide = require('./slide')
|
||||
const rateLimit = require('../middleware/rateLimit')
|
||||
const config = require('../../config')
|
||||
|
||||
const applyRateLimitIfConfigured = (req, res, next) => {
|
||||
if (config.rateLimitNewNotes > 0) {
|
||||
return rateLimit.newNotes(req, res, next)
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
// get new note
|
||||
router.get('/new', noteController.createFromPOST)
|
||||
router.get('/new', applyRateLimitIfConfigured, noteController.createFromPOST)
|
||||
// post new note with content
|
||||
router.post('/new', markdownParser, noteController.createFromPOST)
|
||||
router.post('/new', applyRateLimitIfConfigured, markdownParser, noteController.createFromPOST)
|
||||
// post new note with content and alias
|
||||
router.post('/new/:noteId', markdownParser, noteController.createFromPOST)
|
||||
router.post('/new/:noteId', applyRateLimitIfConfigured, markdownParser, noteController.createFromPOST)
|
||||
// get publish note
|
||||
router.get('/s/:shortid', noteController.showPublishNote)
|
||||
// publish note actions
|
||||
|
||||
Reference in New Issue
Block a user