diff --git a/middleware.test.ts b/middleware.test.ts index 4cac066a..cfe86b87 100644 --- a/middleware.test.ts +++ b/middleware.test.ts @@ -171,7 +171,7 @@ describe('middleware', () => { expect(rateLimit).toHaveBeenCalledWith('127.0.0.1', 60, 60000); }); - it('prefers x-forwarded-for over x-real-ip', async () => { + it('prefers x-real-ip over x-forwarded-for to prevent spoofing', async () => { vi.mocked(rateLimit).mockResolvedValue({ success: true, limit: 60, @@ -188,7 +188,8 @@ describe('middleware', () => { await middleware(request); - expect(rateLimit).toHaveBeenCalledWith('1.2.3.4', 60, 60000); + // Expect 9.9.9.9 instead of 1.2.3.4 because x-real-ip is more secure + expect(rateLimit).toHaveBeenCalledWith('9.9.9.9', 60, 60000); }); it('handles multiple IPs with whitespace', async () => { diff --git a/middleware.ts b/middleware.ts index 9ea114e6..e5746819 100644 --- a/middleware.ts +++ b/middleware.ts @@ -5,7 +5,6 @@ import { getClientIp } from './utils/getClientIp'; /** * Middleware to enforce rate limiting on specific API routes. - * * Protected Routes: * - /api/streak * - /api/github @@ -17,8 +16,12 @@ import { getClientIp } from './utils/getClientIp'; * Limit: 60 requests per minute per IP. */ export async function middleware(request: NextRequest) { - // Secure client IP extraction - const ip = getClientIp(request); + // 1. Fallback to `x-real-ip` (securely set by reverse proxies like Nginx/Vercel). + // 2. Fallback to `x-forwarded-for` properly parsed, or localhost. + const ip = + request.headers.get('x-real-ip') ?? + request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? + '127.0.0.1'; // Apply rate limiting // 60 requests per 60,000ms (1 minute) @@ -62,4 +65,4 @@ export const config = { '/api/notify/:path*', '/api/compare/:path*', ], -}; +}; \ No newline at end of file