proxy.ts
1 import { NextResponse } from 'next/server'; 2 import type { NextRequest } from 'next/server'; 3 4 export function proxy(request: NextRequest) { 5 // Only check state-changing methods 6 if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(request.method)) { 7 const origin = request.headers.get('origin'); 8 const host = request.headers.get('host'); 9 const referer = request.headers.get('referer'); 10 11 // If Origin is present, it must match the Host (protocol + hostname + port) 12 // Note: host header usually doesn't include protocol, but origin does. 13 // Construct expected origin from protocol and host. 14 if (origin && host) { 15 // In production behind proxies, protocol might need 'x-forwarded-proto' 16 const protocol = request.headers.get('x-forwarded-proto') || request.nextUrl.protocol.replace(':', ''); 17 const expectedOrigin = `${protocol}://${host}`; 18 19 if (origin !== expectedOrigin) { 20 // Also allow if origin is just the localhost with http 21 // (Simpler check: does origin end with host?) 22 try { 23 const originUrl = new URL(origin); 24 if (originUrl.host !== host) { 25 console.warn(`CSRF Blocked: Origin ${origin} does not match Host ${host}`); 26 return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 }); 27 } 28 } catch { 29 console.warn(`CSRF Blocked: Invalid Origin ${origin}`); 30 return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 }); 31 } 32 } 33 } 34 35 // Strict Referer check as fallback/addition 36 if (referer && host) { 37 try { 38 const refererUrl = new URL(referer); 39 if (refererUrl.host !== host) { 40 console.warn(`CSRF Blocked: Referer ${referer} does not match Host ${host}`); 41 return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 }); 42 } 43 } catch { 44 console.warn(`CSRF Blocked: Invalid Referer ${referer}`); 45 return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 }); 46 } 47 } 48 } 49 50 return NextResponse.next(); 51 } 52 53 export const config = { 54 matcher: '/api/:path*', 55 };