From c072fd657dcc5139327a0660566d84fefec41450 Mon Sep 17 00:00:00 2001 From: Erik Michelson Date: Wed, 13 May 2026 22:46:45 +0200 Subject: [PATCH] fix(rate-limit): increase auth limit defaults This is a moderate increase from about 2 req/minute to 2.6 req/minute with an increase of the window to 15 minutes. Switching between accounts a few times should be covered by the higher rate limit. At the same time, the window increase reduces the attack/abuse chance again. Fixes #6471 Signed-off-by: Erik Michelson --- backend/src/config/mock/security.config.mock.ts | 4 ++-- backend/src/config/security.config.spec.ts | 12 ++++++------ backend/src/config/security.config.ts | 4 ++-- backend/src/security/rate-limiting.spec.ts | 6 +++--- docs/content/references/config/security.md | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/backend/src/config/mock/security.config.mock.ts b/backend/src/config/mock/security.config.mock.ts index c24322a63..d0d2e3b6b 100644 --- a/backend/src/config/mock/security.config.mock.ts +++ b/backend/src/config/mock/security.config.mock.ts @@ -24,8 +24,8 @@ export function createDefaultMockSecurityConfig(): SecurityConfig { window: 300, }, auth: { - max: 20, - window: 600, + max: 40, + window: 900, }, bypass: [], }, diff --git a/backend/src/config/security.config.spec.ts b/backend/src/config/security.config.spec.ts index 3a034dd2f..e8600cfbc 100644 --- a/backend/src/config/security.config.spec.ts +++ b/backend/src/config/security.config.spec.ts @@ -18,8 +18,8 @@ describe('securityConfig: rate limiting', () => { HD_SECURITY_RATE_LIMIT_AUTHENTICATED_WINDOW: '300', HD_SECURITY_RATE_LIMIT_UNAUTHENTICATED_MAX: '100', HD_SECURITY_RATE_LIMIT_UNAUTHENTICATED_WINDOW: '300', - HD_SECURITY_RATE_LIMIT_AUTH_MAX: '20', - HD_SECURITY_RATE_LIMIT_AUTH_WINDOW: '600', + HD_SECURITY_RATE_LIMIT_AUTH_MAX: '40', + HD_SECURITY_RATE_LIMIT_AUTH_WINDOW: '900', HD_SECURITY_RATE_LIMIT_BYPASS: '127.0.0.1,::1', /* oxlint-enable @typescript-eslint/naming-convention */ }; @@ -41,8 +41,8 @@ describe('securityConfig: rate limiting', () => { expect(config.rateLimit.authenticated.window).toEqual(300); expect(config.rateLimit.unauthenticated.max).toEqual(100); expect(config.rateLimit.unauthenticated.window).toEqual(300); - expect(config.rateLimit.auth.max).toEqual(20); - expect(config.rateLimit.auth.window).toEqual(600); + expect(config.rateLimit.auth.max).toEqual(40); + expect(config.rateLimit.auth.window).toEqual(900); expect(config.rateLimit.bypass).toEqual(['127.0.0.1', '::1']); restore(); }); @@ -61,8 +61,8 @@ describe('securityConfig: rate limiting', () => { expect(config.rateLimit.authenticated.window).toEqual(300); expect(config.rateLimit.unauthenticated.max).toEqual(100); expect(config.rateLimit.unauthenticated.window).toEqual(300); - expect(config.rateLimit.auth.max).toEqual(20); - expect(config.rateLimit.auth.window).toEqual(600); + expect(config.rateLimit.auth.max).toEqual(40); + expect(config.rateLimit.auth.window).toEqual(900); expect(config.rateLimit.bypass).toEqual([]); restore(); }); diff --git a/backend/src/config/security.config.ts b/backend/src/config/security.config.ts index 7ab7b3b04..ada87db61 100644 --- a/backend/src/config/security.config.ts +++ b/backend/src/config/security.config.ts @@ -54,12 +54,12 @@ const securityConfigSchema = z.object({ .describe('HD_SECURITY_RATE_LIMIT_UNAUTHENTICATED_WINDOW'), }), auth: z.object({ - max: z.number().int().nonnegative().default(20).describe('HD_SECURITY_RATE_LIMIT_AUTH_MAX'), + max: z.number().int().nonnegative().default(40).describe('HD_SECURITY_RATE_LIMIT_AUTH_MAX'), window: z .number() .int() .positive() - .default(600) + .default(900) .describe('HD_SECURITY_RATE_LIMIT_AUTH_WINDOW'), }), bypass: z diff --git a/backend/src/security/rate-limiting.spec.ts b/backend/src/security/rate-limiting.spec.ts index 652365c1e..2cbe76924 100644 --- a/backend/src/security/rate-limiting.spec.ts +++ b/backend/src/security/rate-limiting.spec.ts @@ -24,7 +24,7 @@ describe('rate limiting', () => { publicApi: { max: 150, window: 300 }, authenticated: { max: 600, window: 300 }, unauthenticated: { max: 100, window: 300 }, - auth: { max: 20, window: 600 }, + auth: { max: 40, window: 900 }, }, }); @@ -95,8 +95,8 @@ describe('rate limiting', () => { it('uses auth limits for auth endpoints', () => { const request = createMockedRequest({ url: '/api/private/auth/login' }); - expect(getTimeWindowByRequestWithSecurityConfig(securityConfig)(request, 'key')).toBe(600000); - expect(getMaxLimitByRequestWithSecurityConfig(securityConfig)(request, 'key')).toBe(20); + expect(getTimeWindowByRequestWithSecurityConfig(securityConfig)(request, 'key')).toBe(900000); + expect(getMaxLimitByRequestWithSecurityConfig(securityConfig)(request, 'key')).toBe(40); }); it('returns infinity when the configured max is zero', () => { diff --git a/docs/content/references/config/security.md b/docs/content/references/config/security.md index 87a6da380..025c43836 100644 --- a/docs/content/references/config/security.md +++ b/docs/content/references/config/security.md @@ -45,6 +45,6 @@ Setting a `*_MAX` value to `0` effectively disables rate limiting for that tier | `HD_SECURITY_RATE_LIMIT_AUTHENTICATED_WINDOW` | 300 | Time window in seconds for authenticated usage | | `HD_SECURITY_RATE_LIMIT_UNAUTHENTICATED_MAX` | 100 | Maximum requests for unauthenticated usage | | `HD_SECURITY_RATE_LIMIT_UNAUTHENTICATED_WINDOW` | 300 | Time window in seconds for unauthenticated usage | -| `HD_SECURITY_RATE_LIMIT_AUTH_MAX` | 20 | Maximum of auth request attempts | -| `HD_SECURITY_RATE_LIMIT_AUTH_WINDOW` | 600 | Time window in seconds for auth request attempts | +| `HD_SECURITY_RATE_LIMIT_AUTH_MAX` | 40 | Maximum of auth request attempts | +| `HD_SECURITY_RATE_LIMIT_AUTH_WINDOW` | 900 | Time window in seconds for auth request attempts | | `HD_SECURITY_RATE_LIMIT_BYPASS` | *none* | Bypass rate limiting for these IP addresses (comma-separated list) |