← back to Wine Finder Next
lib/advancedSecurity.ts.backup
357 lines
// Advanced Security Features - Enhanced Protection Layer
import { logSecurityEvent, blockIP } from './securityMonitoring';
interface RequestFingerprint {
userAgent: string;
acceptLanguage: string;
acceptEncoding: string;
connection: string;
deviceType: string;
browserFingerprint: string;
}
interface GeoLocation {
country: string;
region: string;
city: string;
isHighRisk: boolean;
}
// High-risk countries for fraud (based on common fraud patterns)
const HIGH_RISK_COUNTRIES = [
'CN', 'RU', 'NG', 'PK', 'VN', 'ID', 'IN', // Known for high bot/fraud activity
'UA', 'RO', 'BR', 'TR', 'MY', 'TH', 'PH' // Additional risk regions
];
// Known malicious IP ranges (examples - expand in production)
const BLOCKED_IP_RANGES = [
{ start: '0.0.0.0', end: '0.255.255.255' }, // Reserved
{ start: '127.0.0.0', end: '127.255.255.255' }, // Loopback
{ start: '10.0.0.0', end: '10.255.255.255' }, // Private
];
// Bot user agent patterns
const BOT_USER_AGENTS = [
/bot|crawler|spider|scraper|curl|wget|python|java|golang/i,
/headless|phantom|selenium|puppeteer|playwright/i,
/axios|fetch|http|request|postman/i,
/scanner|vulnerability|nikto|nmap|sqlmap/i,
/masscan|shodan|censys|zgrab/i
];
// Suspicious header patterns
const SUSPICIOUS_HEADERS = {
missingUserAgent: (headers: any) => !headers['user-agent'],
invalidAccept: (headers: any) => !headers['accept'] || headers['accept'] === '*/*',
noReferer: (headers: any) => !headers['referer'] && !headers['origin'],
suspiciousUserAgent: (headers: any) => {
const ua = headers['user-agent'] || '';
return BOT_USER_AGENTS.some(pattern => pattern.test(ua));
}
};
// Request rate tracking per fingerprint
const fingerprintRequests = new Map<string, { count: number; firstSeen: Date; lastSeen: Date }>();
// Detect bot traffic
export function detectBot(userAgent: string): boolean {
if (!userAgent) return true; // No user agent = likely bot
// Check against known bot patterns
return BOT_USER_AGENTS.some(pattern => pattern.test(userAgent));
}
// Generate request fingerprint
export function generateRequestFingerprint(headers: any): string {
const components = [
headers['user-agent'] || 'none',
headers['accept-language'] || 'none',
headers['accept-encoding'] || 'none',
headers['accept'] || 'none',
headers['connection'] || 'none'
];
// Simple hash of combined headers
const fingerprint = components.join('|');
let hash = 0;
for (let i = 0; i < fingerprint.length; i++) {
const char = fingerprint.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash.toString(16);
}
// Detect suspicious request patterns
export function detectSuspiciousRequest(headers: any, ip: string): {
suspicious: boolean;
reasons: string[];
riskScore: number;
} {
const reasons: string[] = [];
let riskScore = 0;
// Check for missing/suspicious headers
if (SUSPICIOUS_HEADERS.missingUserAgent(headers)) {
reasons.push('MISSING_USER_AGENT');
riskScore += 30;
}
if (SUSPICIOUS_HEADERS.invalidAccept(headers)) {
reasons.push('INVALID_ACCEPT_HEADER');
riskScore += 20;
}
if (SUSPICIOUS_HEADERS.suspiciousUserAgent(headers)) {
reasons.push('BOT_USER_AGENT');
riskScore += 50;
}
// Check fingerprint frequency
const fingerprint = generateRequestFingerprint(headers);
const fpData = fingerprintRequests.get(fingerprint);
if (fpData) {
const timeDiff = Date.now() - fpData.firstSeen.getTime();
const requestsPerMinute = (fpData.count / timeDiff) * 60000;
if (requestsPerMinute > 100) {
reasons.push('EXCESSIVE_REQUEST_RATE');
riskScore += 40;
}
fpData.count++;
fpData.lastSeen = new Date();
} else {
fingerprintRequests.set(fingerprint, {
count: 1,
firstSeen: new Date(),
lastSeen: new Date()
});
}
// Check for tor/proxy headers (but not regular reverse proxy headers)
if (headers['x-tor-exit-node']) {
reasons.push('TOR_DETECTED');
riskScore += 25;
}
// Don't penalize normal proxy headers like x-forwarded-for (used by reverse proxies)
return {
suspicious: riskScore >= 50,
reasons,
riskScore
};
}
// Simulate basic geolocation (in production, use MaxMind GeoIP2 or similar)
export function getGeoLocation(ip: string): GeoLocation {
// This is a placeholder - in production use real GeoIP service
// Allow localhost and private IPs
if (ip === '::1' || ip === '127.0.0.1' || ip.startsWith('192.168.') || ip.startsWith('10.') ||
ip.startsWith('::ffff:127.0.0.1') || ip.startsWith('::ffff:45.61.58.125') || ip === 'unknown') {
return {
country: 'US',
region: 'California',
city: 'San Francisco',
isHighRisk: false
};
}
// Default to low risk for safety (to avoid blocking legitimate traffic)
// In production with real GeoIP, this should check against HIGH_RISK_COUNTRIES
return {
country: 'XX',
region: 'Unknown',
city: 'Unknown',
isHighRisk: false // Changed to false to reduce false positives
};
}
// Check if country is high risk
export function isHighRiskCountry(countryCode: string): boolean {
return HIGH_RISK_COUNTRIES.includes(countryCode.toUpperCase());
}
// Advanced DDoS detection
export function detectDDoS(ip: string): {
isDDoS: boolean;
reason: string;
blockDuration?: number;
} {
// Track requests per IP
const now = Date.now();
const oneMinute = 60000;
// Get fingerprint data for this IP
const fingerprint = fingerprintRequests.get(ip);
if (!fingerprint) {
return { isDDoS: false, reason: 'NEW_IP' };
}
const timeDiff = now - fingerprint.firstSeen.getTime();
const requestsPerSecond = (fingerprint.count / timeDiff) * 1000;
// DDoS thresholds
if (requestsPerSecond > 50) {
return {
isDDoS: true,
reason: 'EXTREME_REQUEST_RATE',
blockDuration: 3600000 // Block for 1 hour
};
}
if (requestsPerSecond > 10 && fingerprint.count > 100) {
return {
isDDoS: true,
reason: 'SUSTAINED_HIGH_RATE',
blockDuration: 600000 // Block for 10 minutes
};
}
return { isDDoS: false, reason: 'NORMAL_TRAFFIC' };
}
// Comprehensive security check for all requests
export function comprehensiveSecurityCheck(req: {
ip: string;
headers: any;
method: string;
path: string;
}): {
allowed: boolean;
reason: string;
riskScore: number;
actions: string[];
} {
const actions: string[] = [];
let totalRiskScore = 0;
// 1. Check for bot traffic (reduced penalty for common tools like curl)
const isBot = detectBot(req.headers['user-agent']);
if (isBot) {
totalRiskScore += 20; // Reduced from 40 to allow testing tools
actions.push('BOT_DETECTED');
}
// 2. Check for suspicious patterns
const suspicious = detectSuspiciousRequest(req.headers, req.ip);
totalRiskScore += suspicious.riskScore;
actions.push(...suspicious.reasons);
// 3. Check geolocation
const geo = getGeoLocation(req.ip);
if (geo.isHighRisk) {
totalRiskScore += 30;
actions.push('HIGH_RISK_COUNTRY');
}
// 4. Check for DDoS
const ddos = detectDDoS(req.ip);
if (ddos.isDDoS) {
totalRiskScore += 100; // Instant block
actions.push('DDOS_DETECTED');
// Auto-block IP
blockIP(req.ip, `DDoS attack detected: ${ddos.reason}`);
logSecurityEvent({
type: 'SUSPICIOUS_ACTIVITY',
severity: 'critical',
ipAddress: req.ip,
userAgent: req.headers['user-agent'] || 'unknown',
details: {
reason: ddos.reason,
blockDuration: ddos.blockDuration,
method: req.method,
path: req.path
}
});
}
// 5. Check HTTP method
if (['DELETE', 'PUT', 'TRACE', 'CONNECT'].includes(req.method)) {
totalRiskScore += 20;
actions.push('UNUSUAL_HTTP_METHOD');
}
// Decision - increased threshold to reduce false positives
// Allow more traffic through, only block truly suspicious requests
const allowed = totalRiskScore < 100;
if (!allowed) {
logSecurityEvent({
type: 'SUSPICIOUS_ACTIVITY',
severity: totalRiskScore > 150 ? 'critical' : 'high',
ipAddress: req.ip,
userAgent: req.headers['user-agent'] || 'unknown',
details: {
riskScore: totalRiskScore,
actions,
method: req.method,
path: req.path
}
});
}
return {
allowed,
reason: actions.join(', '),
riskScore: totalRiskScore,
actions
};
}
// Clean up old fingerprint data (call periodically)
export function cleanupFingerprintData(): void {
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
for (const [fingerprint, data] of fingerprintRequests.entries()) {
if (data.lastSeen.getTime() < oneDayAgo) {
fingerprintRequests.delete(fingerprint);
}
}
}
// Run cleanup every hour
setInterval(cleanupFingerprintData, 60 * 60 * 1000);
// Security dashboard data
export function getAdvancedSecurityStats(): {
totalFingerprints: number;
activeBots: number;
highRiskRequests: number;
ddosAttempts: number;
averageRiskScore: number;
} {
let activeBots = 0;
let highRiskRequests = 0;
let totalRisk = 0;
for (const [fingerprint, data] of fingerprintRequests.entries()) {
// Simple heuristic: if more than 50 requests/minute, likely bot
const timeDiff = Date.now() - data.firstSeen.getTime();
const requestsPerMinute = (data.count / timeDiff) * 60000;
if (requestsPerMinute > 50) {
activeBots++;
}
if (requestsPerMinute > 10) {
highRiskRequests++;
totalRisk += 50;
}
}
return {
totalFingerprints: fingerprintRequests.size,
activeBots,
highRiskRequests,
ddosAttempts: 0, // Would track from DDoS detection
averageRiskScore: fingerprintRequests.size > 0 ? totalRisk / fingerprintRequests.size : 0
};
}