This commit is contained in:
Iliyan Angelov
2025-12-10 01:36:00 +02:00
parent 2f6dca736a
commit 6a9e823402
84 changed files with 5293 additions and 1836 deletions

139
frontEnd/middleware.ts Normal file
View 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)).*)',
],
};