From 9bf59280b23e4e0bfa968ece80d3fa17d4786d7a Mon Sep 17 00:00:00 2001 From: elpatron Date: Tue, 2 Jun 2026 22:52:52 +0200 Subject: [PATCH] Apply strict rate limits to sensitive auth endpoints. Account deletion, key enrollment, and credential management use a separate 30/15min limiter so they are not left at 300/min while login and sync routes stay independent. Co-authored-by: Cursor --- server/src/app.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index 1077715..cb28084 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -45,7 +45,7 @@ export function createApp(): express.Express { app.use(cookieParser()) app.use(express.json({ limit: '50mb' })) - /** Passkey login/register only — not person-pool, profile, etc. */ + /** WebAuthn login/register/session — strict per IP; excludes high-volume sync routes. */ const authFlowPaths = new Set([ '/register-options', '/register-verify', @@ -57,13 +57,33 @@ export function createApp(): express.Express { '/session' ]) - const authLimiter = rateLimit({ + /** Account/key/credential mutations — also strict; separate bucket from login flow. */ + const sensitiveAuthExactPaths = new Set([ + '/delete-account', + '/enroll-prf', + '/rotate-recovery', + '/add-credential-options', + '/add-credential-verify' + ]) + + function isSensitiveAuthPath(path: string): boolean { + return sensitiveAuthExactPaths.has(path) || path.startsWith('/credentials/') + } + + const authFlowLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 60, standardHeaders: true, legacyHeaders: false }) + const sensitiveAuthLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 30, + standardHeaders: true, + legacyHeaders: false + }) + const apiLimiter = rateLimit({ windowMs: 1 * 60 * 1000, max: 300, @@ -80,7 +100,10 @@ export function createApp(): express.Express { app.use('/api/auth', (req, res, next) => { if (authFlowPaths.has(req.path)) { - return authLimiter(req, res, next) + return authFlowLimiter(req, res, next) + } + if (isSensitiveAuthPath(req.path)) { + return sensitiveAuthLimiter(req, res, next) } return next() })