feat: add profile request rate limiter

This commit is contained in:
Yarmo Mackenbach 2023-09-22 12:15:10 +02:00
parent bccd5d298f
commit b333365730
No known key found for this signature in database
GPG key ID: 3C57D093219103A3
5 changed files with 50 additions and 8 deletions

View file

@ -12,6 +12,7 @@
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"express": "^4.17.1", "express": "^4.17.1",
"express-http-context2": "^1.0.0", "express-http-context2": "^1.0.0",
"express-rate-limit": "^7.0.1",
"express-validator": "^6.13.0", "express-validator": "^6.13.0",
"got": "^11.8.2", "got": "^11.8.2",
"hash-wasm": "^4.9.0", "hash-wasm": "^4.9.0",

View file

@ -29,18 +29,42 @@ more information on this, and how to apply and follow the GNU AGPL, see <https:/
*/ */
import express from 'express' import express from 'express'
import bodyParserImport from 'body-parser' import bodyParserImport from 'body-parser'
import { rateLimit } from 'express-rate-limit'
import { generateSignatureProfile, utils, generateWKDProfile, generateHKPProfile, generateAutoProfile, generateKeybaseProfile } from '../server/index.js' import { generateSignatureProfile, utils, generateWKDProfile, generateHKPProfile, generateAutoProfile, generateKeybaseProfile } from '../server/index.js'
import { Profile } from 'doipjs' import { Profile } from 'doipjs'
import { getMetaFromReq } from '../server/utils.js' import { getMetaFromReq } from '../server/utils.js'
import logger from '../log.js'
const router = express.Router() const router = express.Router()
const bodyParser = bodyParserImport.urlencoded({ extended: false }) const bodyParser = bodyParserImport.urlencoded({ extended: false })
router.get('/sig', (req, res) => { let profileRateLimiter = (req, res, next) => {
next()
}
if (process.env.ENABLE_EXPERIMENTAL_RATE_LIMITER) {
profileRateLimiter = rateLimit({
windowMs: 1000,
limit: 3,
standardHeaders: 'draft-7',
legacyHeaders: false,
handler: (req, res, next, options) => {
logger.debug('Rate-limiting a profile request',
{ component: 'profile_rate_limiter', action: 'block' })
res.status(options.statusCode).render('429', { meta: getMetaFromReq(req) })
}
})
logger.debug('Starting the profile request rate limiter',
{ component: 'profile_rate_limiter', action: 'start' })
}
router.get('/sig', profileRateLimiter, (req, res) => {
res.render('profile', { isSignature: true, signature: null, meta: getMetaFromReq(req) }) res.render('profile', { isSignature: true, signature: null, meta: getMetaFromReq(req) })
}) })
router.post('/sig', bodyParser, async (req, res) => { router.post('/sig', profileRateLimiter, bodyParser, async (req, res) => {
const data = await generateSignatureProfile(req.body.signature) const data = await generateSignatureProfile(req.body.signature)
const title = utils.generatePageTitle('profile', data) const title = utils.generatePageTitle('profile', data)
res.set('ariadne-identity-proof', data.identifier) res.set('ariadne-identity-proof', data.identifier)
@ -55,7 +79,7 @@ router.post('/sig', bodyParser, async (req, res) => {
}) })
}) })
router.get('/wkd/:id', async (req, res) => { router.get('/wkd/:id', profileRateLimiter, async (req, res) => {
const data = await generateWKDProfile(req.params.id) const data = await generateWKDProfile(req.params.id)
const title = utils.generatePageTitle('profile', data) const title = utils.generatePageTitle('profile', data)
res.set('ariadne-identity-proof', data.identifier) res.set('ariadne-identity-proof', data.identifier)
@ -68,7 +92,7 @@ router.get('/wkd/:id', async (req, res) => {
}) })
}) })
router.get('/hkp/:id', async (req, res) => { router.get('/hkp/:id', profileRateLimiter, async (req, res) => {
const data = await generateHKPProfile(req.params.id) const data = await generateHKPProfile(req.params.id)
const title = utils.generatePageTitle('profile', data) const title = utils.generatePageTitle('profile', data)
res.set('ariadne-identity-proof', data.identifier) res.set('ariadne-identity-proof', data.identifier)
@ -81,7 +105,7 @@ router.get('/hkp/:id', async (req, res) => {
}) })
}) })
router.get('/hkp/:server/:id', async (req, res) => { router.get('/hkp/:server/:id', profileRateLimiter, async (req, res) => {
const data = await generateHKPProfile(req.params.id, req.params.server) const data = await generateHKPProfile(req.params.id, req.params.server)
const title = utils.generatePageTitle('profile', data) const title = utils.generatePageTitle('profile', data)
res.set('ariadne-identity-proof', data.identifier) res.set('ariadne-identity-proof', data.identifier)
@ -94,7 +118,7 @@ router.get('/hkp/:server/:id', async (req, res) => {
}) })
}) })
router.get('/keybase/:username/:fingerprint', async (req, res) => { router.get('/keybase/:username/:fingerprint', profileRateLimiter, async (req, res) => {
const data = await generateKeybaseProfile(req.params.username, req.params.fingerprint) const data = await generateKeybaseProfile(req.params.username, req.params.fingerprint)
const title = utils.generatePageTitle('profile', data) const title = utils.generatePageTitle('profile', data)
res.set('ariadne-identity-proof', data.identifier) res.set('ariadne-identity-proof', data.identifier)
@ -107,7 +131,7 @@ router.get('/keybase/:username/:fingerprint', async (req, res) => {
}) })
}) })
router.get('/:id', async (req, res) => { router.get('/:id', profileRateLimiter, async (req, res) => {
const data = await generateAutoProfile(req.params.id) const data = await generateAutoProfile(req.params.id)
const title = utils.generatePageTitle('profile', data) const title = utils.generatePageTitle('profile', data)
res.set('ariadne-identity-proof', data.identifier) res.set('ariadne-identity-proof', data.identifier)

View file

@ -36,4 +36,8 @@
# Enable caching of keys (experimental) # Enable caching of keys (experimental)
# Opt-in; to disable, omit the environment variable # Opt-in; to disable, omit the environment variable
#ENABLE_EXPERIMENTAL_CACHE= #ENABLE_EXPERIMENTAL_CACHE=true
# Enable profile request rate limiting (experimental)
# Opt-in; to disable, omit the environment variable
#ENABLE_EXPERIMENTAL_RATE_LIMITER=true

8
views/429.pug Normal file
View file

@ -0,0 +1,8 @@
extends templates/base.pug
block content
h1 429 TOO MANY REQUESTS
p
| Too many requests from this IP, please try again later.
br
| Limit: 3 profile requests per second.

View file

@ -2269,6 +2269,11 @@ express-http-context2@^1.0.0:
resolved "https://registry.yarnpkg.com/express-http-context2/-/express-http-context2-1.0.0.tgz#58cd9fb0d233739e0dcd7aabb766d1dc74522d77" resolved "https://registry.yarnpkg.com/express-http-context2/-/express-http-context2-1.0.0.tgz#58cd9fb0d233739e0dcd7aabb766d1dc74522d77"
integrity sha512-xdukoNNpWcuMn5ZJcjDe/tA+2A96rQ1MyAB/oWUU7qP15Tkz3txQyFsw/QG8YgRzTJ1sNAA8Bdq0o5b/1Y4zLA== integrity sha512-xdukoNNpWcuMn5ZJcjDe/tA+2A96rQ1MyAB/oWUU7qP15Tkz3txQyFsw/QG8YgRzTJ1sNAA8Bdq0o5b/1Y4zLA==
express-rate-limit@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.0.1.tgz#933af24166990ea4fc8004335e6cd6c86fd31562"
integrity sha512-oTIPm094gh8c7nbShl4TNLqnayzOcbDGY7dCRnFqUAvptyb0pp5231LaH34JtvVEbZlOJMiixikU5AVK8VN3FA==
express-validator@^6.10.0, express-validator@^6.13.0: express-validator@^6.10.0, express-validator@^6.13.0:
version "6.15.0" version "6.15.0"
resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-6.15.0.tgz#5e4601428960b0d66f5f4ae09cb32ed2077374a4" resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-6.15.0.tgz#5e4601428960b0d66f5f4ae09cb32ed2077374a4"