Skip to content

Comments

security: add rate limiting to auth endpoints#662

Open
mmcintosh wants to merge 1 commit intoSonicJs-Org:mainfrom
mmcintosh:hotfix/rate-limiting-auth
Open

security: add rate limiting to auth endpoints#662
mmcintosh wants to merge 1 commit intoSonicJs-Org:mainfrom
mmcintosh:hotfix/rate-limiting-auth

Conversation

@mmcintosh
Copy link
Contributor

Security: Add Rate Limiting to Auth Endpoints

Summary

Adds KV-based rate limiting middleware to all authentication endpoints, preventing brute-force login attempts and registration abuse. Gracefully degrades when KV binding is unavailable.

Changes

1. Rate Limiting Middleware

  • KV-based sliding window using CACHE_KV binding
  • Key format: ratelimit:<prefix>:<ip> with auto-expiring TTL
  • IP detection via cf-connecting-ip header (Cloudflare), x-forwarded-for fallback
  • Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • Returns 429 Too Many Requests with Retry-After header when exceeded

2. Auth Endpoints Protected

  • POST /auth/login and /auth/login/form -- 5 requests/minute per IP
  • POST /auth/register and /auth/register/form -- 3 requests/minute per IP
  • POST /auth/seed-admin -- 2 requests/minute per IP
  • POST /auth/request-password-reset -- 3 requests/15 minutes per IP

3. Graceful Degradation

  • Skips silently if CACHE_KV binding is not available
  • Catches all errors as non-fatal -- rate limiting never breaks the app

Technical Details

New File:

  • packages/core/src/middleware/rate-limit.ts -- rateLimit({ max, windowMs, keyPrefix }) middleware factory

Updated Files:

  • packages/core/src/middleware/index.ts -- Export rateLimit
  • packages/core/src/routes/auth.ts -- Applied rateLimit() to 6 route handlers

Rate Limit Configuration:

Endpoint Max Requests Window Key Prefix
POST /auth/login 5 60s login
POST /auth/login/form 5 60s login
POST /auth/register 3 60s register
POST /auth/register/form 3 60s register
POST /auth/seed-admin 2 60s seed-admin
POST /auth/request-password-reset 3 15min password-reset

Testing

  • Unit Tests: PASSED
  • E2E Tests: PASSED (3/3 shards green)
  • E2E tests run well under rate limit thresholds so no interference

Performance Impact

Metric Impact
Added latency ~1-2ms (single KV read + write per request)
KV operations 2 per auth request (get + put)
Storage Auto-expiring keys, minimal footprint

Breaking Changes

  • Automated scripts hitting auth endpoints more than 5 times/minute from the same IP will get 429 responses. Adjust max values if needed for CI/testing.

Migration Notes

  • Requires CACHE_KV binding for rate limiting to be active. If the binding is missing, all requests pass through (no rate limiting).
  • No database changes needed.

Known Issues

None.

Demo / Screenshots

N/A -- no UI changes.

Related Issues

(Security hardening -- no linked issue)

Checklist

  • Code follows project coding standards
  • Tests added/updated and passing
  • Documentation updated
  • No breaking changes
  • Backward compatible

}

it('valid key with matching scope — calls next()', async () => {
const hash = await hashApiKey(TOKEN)

Check notice

Code scanning / CodeQL

Unused variable, import, function or class

Unused variable hash.
})

const mw = requireApiKey('search:read')
const result = await mw(ctx, mockNext)

Check notice

Code scanning / CodeQL

Unused variable, import, function or class

Unused variable result.
- Create KV-based sliding window rate limiter middleware
- Apply rate limits to auth endpoints:
  - Login (JSON + form): 5 requests/minute
  - Register (JSON + form): 3 requests/minute
  - Password reset: 3 requests/15 minutes
  - Seed admin: 2 requests/minute
- Includes Retry-After and X-RateLimit-* response headers
- Graceful degradation: skips rate limiting if CACHE_KV not available
- Export rateLimit from middleware/index.ts

Fixes VULN-004
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant