← back to Wine Finder Next
lib/securityMonitoring.ts
272 lines
// Security Monitoring and Intrusion Detection
// NOTE: Removed logAudit import to maintain Edge Runtime compatibility
// This file is used by middleware which runs in Edge Runtime
interface SecurityEvent {
type: SecurityEventType;
severity: 'low' | 'medium' | 'high' | 'critical';
userId?: string;
ipAddress: string;
userAgent: string;
details: any;
timestamp: Date;
}
type SecurityEventType =
| 'BRUTE_FORCE_ATTEMPT'
| 'SQL_INJECTION_ATTEMPT'
| 'XSS_ATTEMPT'
| 'RATE_LIMIT_ABUSE'
| 'INVALID_TOKEN'
| 'UNAUTHORIZED_ACCESS'
| 'SUSPICIOUS_ACTIVITY'
| 'DATA_BREACH_ATTEMPT'
| 'ANOMALOUS_BEHAVIOR';
// Event storage
const securityEvents: SecurityEvent[] = [];
const suspiciousIPs: Map<string, { score: number; lastSeen: Date }> = new Map();
const blockedIPs: Set<string> = new Set();
// Detect SQL injection attempts
export function detectSQLInjection(input: string): boolean {
const sqlPatterns = [
/(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE)\b)/i,
/(UNION\s+SELECT)/i,
/(OR\s+1\s*=\s*1)/i,
/(AND\s+1\s*=\s*1)/i,
/(['";]|--|\*|\/\*)/,
/(xp_cmdshell|sp_executesql)/i
];
return sqlPatterns.some(pattern => pattern.test(input));
}
// Detect XSS attempts
export function detectXSS(input: string): boolean {
const xssPatterns = [
/<script[^>]*>.*?<\/script>/i,
/javascript:/i,
/on\w+\s*=/i,
/<iframe/i,
/<embed/i,
/<object/i,
/eval\(/i,
/document\.cookie/i,
/window\.location/i
];
return xssPatterns.some(pattern => pattern.test(input));
}
// Detect path traversal attempts
export function detectPathTraversal(input: string): boolean {
const traversalPatterns = [
/\.\.\//,
/\.\.\\/,
/%2e%2e/i,
/\.\.%2f/i,
/\.\.%5c/i
];
return traversalPatterns.some(pattern => pattern.test(input));
}
// Log security event
export function logSecurityEvent(event: Omit<SecurityEvent, 'timestamp'>): void {
const fullEvent: SecurityEvent = {
...event,
timestamp: new Date()
};
securityEvents.push(fullEvent);
// Audit logging disabled for Edge Runtime compatibility
// (Edge Runtime doesn't support Node.js crypto module)
// In production API routes, events are already logged via logAudit calls
// Update suspicious IP score
updateSuspiciousIPScore(event.ipAddress, event.severity);
// Auto-block on critical events
if (event.severity === 'critical') {
blockIP(event.ipAddress, 'Automatic block due to critical security event');
}
// Alert on high/critical severity
if (['high', 'critical'].includes(event.severity)) {
alertSecurityTeam(fullEvent);
}
}
// Update suspicious IP scoring
function updateSuspiciousIPScore(ipAddress: string, severity: string): void {
const scores = { low: 1, medium: 5, high: 10, critical: 50 };
const score = scores[severity as keyof typeof scores] || 1;
const current = suspiciousIPs.get(ipAddress) || { score: 0, lastSeen: new Date() };
current.score += score;
current.lastSeen = new Date();
suspiciousIPs.set(ipAddress, current);
// Auto-block if score exceeds threshold
if (current.score >= 100) {
blockIP(ipAddress, 'Suspicious activity score exceeded threshold');
}
}
// Block IP address
export function blockIP(ipAddress: string, reason: string): void {
blockedIPs.add(ipAddress);
logSecurityEvent({
type: 'UNAUTHORIZED_ACCESS',
severity: 'high',
ipAddress,
userAgent: 'system',
details: { reason, action: 'IP_BLOCKED' }
});
console.warn(`[SECURITY] IP BLOCKED: ${ipAddress} - ${reason}`);
}
// Check if IP is blocked
export function isIPBlocked(ipAddress: string): boolean {
return blockedIPs.has(ipAddress);
}
// Unblock IP address
export function unblockIP(ipAddress: string): void {
blockedIPs.delete(ipAddress);
suspiciousIPs.delete(ipAddress);
}
// Analyze request for threats
export function analyzeRequest(req: {
body?: any;
query?: any;
params?: any;
headers: any;
ip: string;
}): { safe: boolean; threats: string[] } {
const threats: string[] = [];
// Check if IP is blocked
if (isIPBlocked(req.ip)) {
threats.push('BLOCKED_IP');
return { safe: false, threats };
}
// Analyze all inputs
const inputs = [
...Object.values(req.body || {}),
...Object.values(req.query || {}),
...Object.values(req.params || {})
].filter(v => typeof v === 'string');
for (const input of inputs) {
if (detectSQLInjection(input)) {
threats.push('SQL_INJECTION');
logSecurityEvent({
type: 'SQL_INJECTION_ATTEMPT',
severity: 'high',
ipAddress: req.ip,
userAgent: req.headers['user-agent'] || 'unknown',
details: { input: input.substring(0, 100) }
});
}
if (detectXSS(input)) {
threats.push('XSS');
logSecurityEvent({
type: 'XSS_ATTEMPT',
severity: 'high',
ipAddress: req.ip,
userAgent: req.headers['user-agent'] || 'unknown',
details: { input: input.substring(0, 100) }
});
}
if (detectPathTraversal(input)) {
threats.push('PATH_TRAVERSAL');
logSecurityEvent({
type: 'DATA_BREACH_ATTEMPT',
severity: 'critical',
ipAddress: req.ip,
userAgent: req.headers['user-agent'] || 'unknown',
details: { input: input.substring(0, 100) }
});
}
}
return { safe: threats.length === 0, threats };
}
// Alert security team (in production, send to Slack/email/PagerDuty)
function alertSecurityTeam(event: SecurityEvent): void {
console.error('[SECURITY ALERT]', JSON.stringify(event, null, 2));
// In production:
// - Send to Slack webhook
// - Email security team
// - Trigger PagerDuty alert
// - Log to SIEM system
}
// Get security dashboard data
export function getSecurityDashboard(): {
recentEvents: SecurityEvent[];
suspiciousIPs: Array<{ ip: string; score: number; lastSeen: Date }>;
blockedIPs: string[];
stats: {
total: number;
last24h: number;
bySeverity: Record<string, number>;
};
} {
const last24h = Date.now() - 24 * 60 * 60 * 1000;
const recent24h = securityEvents.filter(e => e.timestamp.getTime() > last24h);
const bySeverity: Record<string, number> = {};
for (const event of securityEvents) {
bySeverity[event.severity] = (bySeverity[event.severity] || 0) + 1;
}
return {
recentEvents: securityEvents.slice(-100),
suspiciousIPs: Array.from(suspiciousIPs.entries()).map(([ip, data]) => ({
ip,
...data
})),
blockedIPs: Array.from(blockedIPs),
stats: {
total: securityEvents.length,
last24h: recent24h.length,
bySeverity
}
};
}
// Cleanup old events
export function cleanupSecurityEvents(): void {
const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000; // 30 days
const filteredEvents = securityEvents.filter(e => e.timestamp.getTime() > cutoff);
securityEvents.length = 0;
securityEvents.push(...filteredEvents);
// Decay suspicious IP scores
for (const [ip, data] of suspiciousIPs.entries()) {
if (Date.now() - data.lastSeen.getTime() > 7 * 24 * 60 * 60 * 1000) {
data.score = Math.max(0, data.score - 10);
if (data.score === 0) {
suspiciousIPs.delete(ip);
}
}
}
}
// Run cleanup periodically
setInterval(cleanupSecurityEvents, 60 * 60 * 1000); // Every hour