updates
This commit is contained in:
139
frontEnd/middleware.ts
Normal file
139
frontEnd/middleware.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { validateRequestIP, getClientIP } from './lib/security/ipWhitelist';
|
||||
import {
|
||||
PROTECTED_PATHS,
|
||||
BLOCKED_USER_AGENTS,
|
||||
BLOCKED_IPS,
|
||||
SUSPICIOUS_PATTERNS,
|
||||
} from './lib/security/config';
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
// Safely get pathname and search
|
||||
let pathname = '/';
|
||||
let search = '';
|
||||
try {
|
||||
if (request?.nextUrl) {
|
||||
pathname = request.nextUrl.pathname || '/';
|
||||
search = request.nextUrl.search || '';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error getting URL from request:', e);
|
||||
}
|
||||
|
||||
// Safely get headers
|
||||
let headersObj: Record<string, string> = {};
|
||||
try {
|
||||
if (request?.headers && typeof request.headers.entries === 'function') {
|
||||
try {
|
||||
headersObj = Object.fromEntries(request.headers.entries());
|
||||
} catch (entriesError) {
|
||||
// If entries() fails, try to manually extract headers
|
||||
console.warn('Error getting header entries, trying alternative method:', entriesError);
|
||||
headersObj = {};
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If headers can't be converted, use empty object
|
||||
console.warn('Error converting headers:', e);
|
||||
}
|
||||
|
||||
// Safely get user agent
|
||||
let userAgent = '';
|
||||
try {
|
||||
if (request?.headers && typeof request.headers.get === 'function') {
|
||||
const ua = request.headers.get('user-agent');
|
||||
userAgent = ua || '';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error getting user agent:', e);
|
||||
}
|
||||
|
||||
const ip = getClientIP(headersObj);
|
||||
|
||||
// Security checks
|
||||
const securityChecks: Array<{ check: () => boolean; action: () => NextResponse }> = [];
|
||||
|
||||
// 1. Block malicious user agents
|
||||
securityChecks.push({
|
||||
check: () => BLOCKED_USER_AGENTS.some(blocked => userAgent.toLowerCase().includes(blocked.toLowerCase())),
|
||||
action: () => {
|
||||
console.warn(`[SECURITY] Blocked malicious user agent: ${userAgent} from IP: ${ip}`);
|
||||
return new NextResponse('Forbidden', { status: 403 });
|
||||
},
|
||||
});
|
||||
|
||||
// 2. Block known malicious IPs
|
||||
securityChecks.push({
|
||||
check: () => BLOCKED_IPS.includes(ip),
|
||||
action: () => {
|
||||
console.warn(`[SECURITY] Blocked known malicious IP: ${ip}`);
|
||||
return new NextResponse('Forbidden', { status: 403 });
|
||||
},
|
||||
});
|
||||
|
||||
// 3. IP whitelist check for protected paths
|
||||
if (PROTECTED_PATHS.some(path => pathname.startsWith(path))) {
|
||||
const validation = validateRequestIP(headersObj);
|
||||
if (!validation.allowed) {
|
||||
console.warn(`[SECURITY] Blocked non-whitelisted IP: ${ip} from accessing: ${pathname}`);
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
error: 'Forbidden',
|
||||
message: 'Access denied from this IP address',
|
||||
}),
|
||||
{
|
||||
status: 403,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Block suspicious query parameters (potential XSS/SQL injection attempts)
|
||||
const fullUrl = pathname + search;
|
||||
if (SUSPICIOUS_PATTERNS.some(pattern => pattern.test(fullUrl))) {
|
||||
console.warn(`[SECURITY] Blocked suspicious request pattern from IP: ${ip} - URL: ${fullUrl}`);
|
||||
return new NextResponse('Bad Request', { status: 400 });
|
||||
}
|
||||
|
||||
// 5. Rate limiting headers (basic implementation)
|
||||
// In production, use a proper rate limiting service
|
||||
const response = NextResponse.next();
|
||||
|
||||
// Add security headers (safely check if headers exist)
|
||||
try {
|
||||
if (response?.headers && typeof response.headers.set === 'function') {
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('X-Frame-Options', 'SAMEORIGIN');
|
||||
response.headers.set('X-XSS-Protection', '1; mode=block');
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error setting response headers:', e);
|
||||
}
|
||||
|
||||
// Execute security checks
|
||||
for (const { check, action } of securityChecks) {
|
||||
if (check()) {
|
||||
return action();
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Configure which routes the middleware runs on
|
||||
export const config = {
|
||||
matcher: [
|
||||
/*
|
||||
* Match all request paths except:
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico (favicon file)
|
||||
* - public files (images, etc.)
|
||||
*/
|
||||
'/((?!_next/static|_next/image|favicon.ico|images|icons|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|woff|woff2|ttf|eot)).*)',
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user