Add middleware to block misbehaving clients
This commit is contained in:
@ -1,7 +1,119 @@
|
|||||||
import { type NextRequest } from 'next/server';
|
import { type NextRequest, NextResponse } from 'next/server';
|
||||||
import { updateSession } from '@/utils/supabase/middleware';
|
import { updateSession } from '@/utils/supabase/middleware';
|
||||||
|
|
||||||
|
// In-memory store for tracking IPs (use Redis in production)
|
||||||
|
const ipAttempts = new Map<string, { count: number; lastAttempt: number }>();
|
||||||
|
const bannedIPs = new Set<string>();
|
||||||
|
|
||||||
|
// Suspicious patterns that indicate malicious activity
|
||||||
|
const MALICIOUS_PATTERNS = [
|
||||||
|
/web-inf/i,
|
||||||
|
/\.jsp/i,
|
||||||
|
/\.php/i,
|
||||||
|
/puttest/i,
|
||||||
|
/WEB-INF/i,
|
||||||
|
/\.xml$/i,
|
||||||
|
/perl/i,
|
||||||
|
/xampp/i,
|
||||||
|
/phpwebgallery/i,
|
||||||
|
/FileManager/i,
|
||||||
|
/standalonemanager/i,
|
||||||
|
/h2console/i,
|
||||||
|
/WebAdmin/i,
|
||||||
|
/login_form\.php/i,
|
||||||
|
/%2e/i,
|
||||||
|
/%u002e/i,
|
||||||
|
/\.%00/i,
|
||||||
|
/\.\./,
|
||||||
|
/lcgi/i,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Suspicious HTTP methods
|
||||||
|
const SUSPICIOUS_METHODS = ['TRACE', 'PUT', 'DELETE', 'PATCH'];
|
||||||
|
|
||||||
|
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
|
||||||
|
const MAX_ATTEMPTS = 10; // Max suspicious requests per window
|
||||||
|
const BAN_DURATION = 30 * 60 * 1000; // 30 minutes
|
||||||
|
|
||||||
|
const getClientIP = (request: NextRequest): string => {
|
||||||
|
const forwarded = request.headers.get('x-forwarded-for');
|
||||||
|
const realIP = request.headers.get('x-real-ip');
|
||||||
|
|
||||||
|
if (forwarded) {
|
||||||
|
return forwarded.split(',')[0].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realIP) {
|
||||||
|
return realIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.ip ?? 'unknown';
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPathSuspicious = (pathname: string): boolean => {
|
||||||
|
return MALICIOUS_PATTERNS.some((pattern) => pattern.test(pathname));
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMethodSuspicious = (method: string): boolean => {
|
||||||
|
return SUSPICIOUS_METHODS.includes(method);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateIPAttempts = (ip: string): boolean => {
|
||||||
|
const now = Date.now();
|
||||||
|
const attempts = ipAttempts.get(ip);
|
||||||
|
|
||||||
|
if (!attempts || now - attempts.lastAttempt > RATE_LIMIT_WINDOW) {
|
||||||
|
ipAttempts.set(ip, { count: 1, lastAttempt: now });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts.count++;
|
||||||
|
attempts.lastAttempt = now;
|
||||||
|
|
||||||
|
if (attempts.count > MAX_ATTEMPTS) {
|
||||||
|
bannedIPs.add(ip);
|
||||||
|
// Clean up the attempts record
|
||||||
|
ipAttempts.delete(ip);
|
||||||
|
|
||||||
|
// Auto-unban after duration (in production, use a proper scheduler)
|
||||||
|
setTimeout(() => {
|
||||||
|
bannedIPs.delete(ip);
|
||||||
|
}, BAN_DURATION);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
export const middleware = async (request: NextRequest) => {
|
export const middleware = async (request: NextRequest) => {
|
||||||
|
const { pathname } = request.nextUrl;
|
||||||
|
const method = request.method;
|
||||||
|
const ip = getClientIP(request);
|
||||||
|
|
||||||
|
// Check if IP is already banned
|
||||||
|
if (bannedIPs.has(ip)) {
|
||||||
|
console.log(`🚫 Blocked banned IP: ${ip} trying to access ${pathname}`);
|
||||||
|
return new NextResponse('Access denied.', { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for suspicious activity
|
||||||
|
const isSuspiciousPath = isPathSuspicious(pathname);
|
||||||
|
const isSuspiciousMethod = isMethodSuspicious(method);
|
||||||
|
|
||||||
|
if (isSuspiciousPath || isSuspiciousMethod) {
|
||||||
|
console.log(`⚠️ Suspicious activity from ${ip}: ${method} ${pathname}`);
|
||||||
|
|
||||||
|
const shouldBan = updateIPAttempts(ip);
|
||||||
|
|
||||||
|
if (shouldBan) {
|
||||||
|
console.log(`🔨 IP ${ip} has been banned for suspicious activity`);
|
||||||
|
return new NextResponse('Access denied - IP banned', { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 404 to not reveal the blocking mechanism
|
||||||
|
return new NextResponse('Not Found', { status: 404 });
|
||||||
|
}
|
||||||
return await updateSession(request);
|
return await updateSession(request);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user