← back to Wine Finder Next
middleware.ts
93 lines
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { comprehensiveSecurityCheck } from './lib/advancedSecurity';
import { isIPBlocked } from './lib/securityMonitoring';
// Security headers
export function middleware(request: NextRequest) {
// Extract client IP
const forwardedFor = request.headers.get('x-forwarded-for');
const realIp = request.headers.get('x-real-ip');
const ip = forwardedFor?.split(',')[0] || realIp || 'unknown';
// Check if IP is blocked
if (isIPBlocked(ip)) {
return NextResponse.json({
success: false,
error: 'Access denied. Your IP has been blocked due to suspicious activity.'
}, { status: 403 });
}
// Run comprehensive security check for API routes
if (request.nextUrl.pathname.startsWith('/api/')) {
const securityCheck = comprehensiveSecurityCheck({
ip,
headers: Object.fromEntries(request.headers),
method: request.method,
path: request.nextUrl.pathname
});
if (!securityCheck.allowed) {
return NextResponse.json({
success: false,
error: 'Security check failed',
reason: securityCheck.reason,
riskScore: securityCheck.riskScore
}, { status: 403 });
}
}
const response = NextResponse.next();
// Security Headers
response.headers.set('X-DNS-Prefetch-Control', 'on');
response.headers.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload');
response.headers.set('X-Frame-Options', 'SAMEORIGIN');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-XSS-Protection', '1; mode=block');
response.headers.set('Referrer-Policy', 'origin-when-cross-origin');
response.headers.set('X-Permitted-Cross-Domain-Policies', 'none');
// Remove server identification
response.headers.delete('X-Powered-By');
response.headers.delete('Server');
// Content Security Policy
response.headers.set(
'Content-Security-Policy',
[
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'", // Next.js requires unsafe-eval for dev
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self' https:",
"frame-ancestors 'self'",
"base-uri 'self'",
"form-action 'self'"
].join('; ')
);
// Permissions Policy (formerly Feature Policy)
response.headers.set(
'Permissions-Policy',
'camera=(), microphone=(), geolocation=(), interest-cohort=()'
);
return response;
}
// Apply to all routes
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - public folder
*/
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
};