Bot Detection and Mitigation: Identifying Automated Traffic
Implement advanced bot detection and mitigation strategies to protect your APIs from automated abuse and malicious traffic.
Table of Contents
Table of Contents
Bot Detection and Mitigation: Identifying Automated Traffic
Bot detection is crucial for protecting APIs from automated abuse while allowing legitimate automation. Implementing effective bot detection requires sophisticated analysis techniques and mitigation strategies.
Bot Detection and Mitigation Overview
Security Threat Landscape
Bot Detection and Mitigation requires careful consideration of multiple factors that impact implementation success and user experience.
Key Considerations
Business Impact
- User experience enhancement
- Operational efficiency gains
- Cost optimization opportunities
- Risk mitigation strategies
Detection Techniques
Advanced bot detection combines multiple signals and analysis techniques to accurately identify automated traffic while minimizing false positives.
Browser Fingerprinting
Device and Browser Analysis
- Canvas fingerprinting for GPU detection
- WebGL parameters and capabilities
- Font enumeration and metrics
- Plugin and extension detection
- Screen resolution and color depth
// Advanced browser fingerprinting service
interface BrowserFingerprint {
canvas: string
webgl: string
fonts: string[]
plugins: string[]
screen: {
width: number
height: number
colorDepth: number
pixelRatio: number
}
timezone: string
language: string
platform: string
cookieEnabled: boolean
doNotTrack?: string
hardwareConcurrency?: number
deviceMemory?: number
}
class FingerprintCollector {
async collectFingerprint(): Promise<BrowserFingerprint> {
const fingerprint: BrowserFingerprint = {
canvas: await this.getCanvasFingerprint(),
webgl: await this.getWebGLFingerprint(),
fonts: await this.getFontList(),
plugins: await this.getPluginList(),
screen: this.getScreenInfo(),
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
language: navigator.language,
platform: navigator.platform,
cookieEnabled: navigator.cookieEnabled,
doNotTrack: navigator.doNotTrack || undefined,
hardwareConcurrency: navigator.hardwareConcurrency,
deviceMemory: (navigator as any).deviceMemory
}
return fingerprint
}
private async getCanvasFingerprint(): Promise<string> {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')!
// Draw a complex pattern
canvas.width = 280
canvas.height = 60
ctx.textBaseline = 'top'
ctx.font = '14px Arial'
ctx.fillStyle = '#f60'
ctx.fillRect(125, 1, 62, 20)
ctx.fillStyle = '#069'
ctx.fillText('🎯 Browser Fingerprint', 2, 15)
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)'
ctx.fillText('Canvas Test String', 4, 35)
// Add noise
for (let i = 0; i < 50; i++) {
ctx.fillStyle = `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.5)`
ctx.fillRect(Math.random() * 280, Math.random() * 60, 1, 1)
}
return canvas.toDataURL()
}
private async getWebGLFingerprint(): Promise<string> {
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
if (!gl) return 'no-webgl'
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info')
const vendor = debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : 'unknown'
const renderer = debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : 'unknown'
return `${vendor}-${renderer}`
}
private async getFontList(): Promise<string[]> {
const baseFonts = ['Arial', 'Courier New', 'Georgia', 'Times New Roman', 'Verdana']
const testString = 'mmmmmmmmmmlli'
const testSize = '72px'
const h = document.getElementsByTagName('body')[0]
const defaultWidth: Record<string, number> = {}
const defaultHeight: Record<string, number> = {}
// Measure base fonts first
for (const font of baseFonts) {
const span = document.createElement('span')
span.style.fontSize = testSize
span.style.fontFamily = font
span.innerHTML = testString
h.appendChild(span)
defaultWidth[font] = span.offsetWidth
defaultHeight[font] = span.offsetHeight
h.removeChild(span)
}
const availableFonts: string[] = []
const fontsToTest = [
'Abadi MT Condensed Light', 'Adobe Fangsong Std', 'Adobe Hebrew',
'Adobe Ming Std', 'Agency FB', 'Aharoni', 'Aldhabi', 'AmeriGothic',
// ... more fonts
]
for (const font of fontsToTest) {
const span = document.createElement('span')
span.style.fontSize = testSize
span.style.fontFamily = font + ',' + baseFonts[0]
span.innerHTML = testString
h.appendChild(span)
const width = span.offsetWidth
const height = span.offsetHeight
h.removeChild(span)
// If dimensions differ from default, font is available
if (width !== defaultWidth[baseFonts[0]] || height !== defaultHeight[baseFonts[0]]) {
availableFonts.push(font)
}
}
return availableFonts
}
private async getPluginList(): Promise<string[]> {
// Note: Plugin detection is deprecated, but still useful for bot detection
const plugins: string[] = []
if (navigator.plugins) {
for (let i = 0; i < navigator.plugins.length; i++) {
plugins.push(navigator.plugins[i].name)
}
}
return plugins
}
private getScreenInfo() {
return {
width: screen.width,
height: screen.height,
colorDepth: screen.colorDepth,
pixelRatio: window.devicePixelRatio || 1
}
}
}
// Usage example
const collector = new FingerprintCollector()
const fingerprint = await collector.collectFingerprint()
console.log('Browser fingerprint collected:', fingerprint)Behavioral Analysis
Mouse and Interaction Patterns
- Mouse movement velocity and patterns
- Click timing and frequency
- Scroll behavior and acceleration
- Form interaction patterns
- Time-based activity analysis
// Behavioral analysis engine
interface UserBehavior {
mouseMovements: Array<{
x: number
y: number
timestamp: number
velocity: number
}>
clicks: Array<{
x: number
y: number
timestamp: number
target: string
timeSincePageLoad: number
}>
scrolls: Array<{
scrollY: number
timestamp: number
velocity: number
}>
keystrokes: Array<{
key: string
timestamp: number
timeBetweenKeystrokes: number
}>
sessionDuration: number
pageViews: number
timeBetweenActions: number[]
}
interface BehaviorScore {
isHuman: boolean
confidence: number // 0-100
suspiciousPatterns: string[]
riskLevel: 'low' | 'medium' | 'high'
}
class BehaviorAnalyzer {
private behaviorData: UserBehavior = {
mouseMovements: [],
clicks: [],
scrolls: [],
keystrokes: [],
sessionDuration: 0,
pageViews: 0,
timeBetweenActions: []
}
private lastActionTime: number = Date.now()
constructor() {
this.initializeTracking()
}
private initializeTracking(): void {
// Mouse movement tracking
document.addEventListener('mousemove', (e) => {
const now = Date.now()
const lastMovement = this.behaviorData.mouseMovements[this.behaviorData.mouseMovements.length - 1]
let velocity = 0
if (lastMovement) {
const distance = Math.sqrt(
Math.pow(e.clientX - lastMovement.x, 2) +
Math.pow(e.clientY - lastMovement.y, 2)
)
const timeDiff = now - lastMovement.timestamp
velocity = distance / timeDiff
}
this.behaviorData.mouseMovements.push({
x: e.clientX,
y: e.clientY,
timestamp: now,
velocity
})
// Keep only recent movements (last 5 minutes)
const fiveMinutesAgo = now - 5 * 60 * 1000
this.behaviorData.mouseMovements = this.behaviorData.mouseMovements.filter(
m => m.timestamp > fiveMinutesAgo
)
this.updateLastActionTime()
})
// Click tracking
document.addEventListener('click', (e) => {
const now = Date.now()
const target = (e.target as Element).tagName.toLowerCase()
this.behaviorData.clicks.push({
x: e.clientX,
y: e.clientY,
timestamp: now,
target,
timeSincePageLoad: now - performance.now()
})
this.updateLastActionTime()
})
// Scroll tracking
let lastScrollY = window.scrollY
window.addEventListener('scroll', () => {
const now = Date.now()
const currentScrollY = window.scrollY
const scrollDistance = currentScrollY - lastScrollY
const timeDiff = now - (this.behaviorData.scrolls[this.behaviorData.scrolls.length - 1]?.timestamp || now)
const velocity = Math.abs(scrollDistance) / Math.max(timeDiff, 1)
this.behaviorData.scrolls.push({
scrollY: currentScrollY,
timestamp: now,
velocity
})
lastScrollY = currentScrollY
this.updateLastActionTime()
})
// Keystroke tracking (only for form inputs)
document.addEventListener('keydown', (e) => {
if (['INPUT', 'TEXTAREA'].includes((e.target as Element).tagName)) {
const now = Date.now()
const lastKeystroke = this.behaviorData.keystrokes[this.behaviorData.keystrokes.length - 1]
const timeBetweenKeystrokes = lastKeystroke ? now - lastKeystroke.timestamp : 0
this.behaviorData.keystrokes.push({
key: e.key,
timestamp: now,
timeBetweenKeystrokes
})
this.updateLastActionTime()
}
})
}
private updateLastActionTime(): void {
const now = Date.now()
const timeDiff = now - this.lastActionTime
if (timeDiff > 100) { // Only track significant delays
this.behaviorData.timeBetweenActions.push(timeDiff)
}
this.lastActionTime = now
}
analyzeBehavior(): BehaviorScore {
const analysis = {
mouseAnalysis: this.analyzeMouseBehavior(),
clickAnalysis: this.analyzeClickBehavior(),
scrollAnalysis: this.analyzeScrollBehavior(),
keystrokeAnalysis: this.analyzeKeystrokeBehavior(),
sessionAnalysis: this.analyzeSessionBehavior()
}
const totalScore = (
analysis.mouseAnalysis.score +
analysis.clickAnalysis.score +
analysis.scrollAnalysis.score +
analysis.keystrokeAnalysis.score +
analysis.sessionAnalysis.score
) / 5
const suspiciousPatterns = [
...analysis.mouseAnalysis.patterns,
...analysis.clickAnalysis.patterns,
...analysis.scrollAnalysis.patterns,
...analysis.keystrokeAnalysis.patterns,
...analysis.sessionAnalysis.patterns
]
const confidence = Math.max(0, Math.min(100, totalScore))
return {
isHuman: confidence > 60,
confidence,
suspiciousPatterns,
riskLevel: confidence > 80 ? 'low' : confidence > 40 ? 'medium' : 'high'
}
}
private analyzeMouseBehavior(): { score: number; patterns: string[] } {
const patterns: string[] = []
let score = 100
// Check for robotic mouse movements
if (this.behaviorData.mouseMovements.length > 0) {
const velocities = this.behaviorData.mouseMovements.map(m => m.velocity)
const avgVelocity = velocities.reduce((a, b) => a + b, 0) / velocities.length
// Too consistent velocity suggests bot
const velocityVariance = this.calculateVariance(velocities)
if (velocityVariance < 0.1 && avgVelocity > 0) {
patterns.push('Unnaturally consistent mouse velocity')
score -= 40
}
// Too many perfectly straight movements
const straightMovements = this.behaviorData.mouseMovements.filter((m, i) => {
if (i === 0) return false
const prev = this.behaviorData.mouseMovements[i - 1]
const dx = m.x - prev.x
const dy = m.y - prev.y
return Math.abs(dx) > 0 && Math.abs(dy) < 2 // Horizontal movement only
}).length
if (straightMovements > this.behaviorData.mouseMovements.length * 0.3) {
patterns.push('Excessive straight-line mouse movements')
score -= 30
}
}
return { score: Math.max(0, score), patterns }
}
private analyzeClickBehavior(): { score: number; patterns: string[] } {
const patterns: string[] = []
let score = 100
if (this.behaviorData.clicks.length > 0) {
// Too fast clicking suggests automation
const clickTimes = this.behaviorData.clicks.map(c => c.timestamp)
const avgTimeBetweenClicks = this.calculateAverageTimeBetween(clickTimes)
if (avgTimeBetweenClicks < 100) { // Less than 100ms between clicks
patterns.push('Unnaturally fast clicking pattern')
score -= 50
}
// Perfect timing suggests bot
const timeVariances = this.calculateTimeVariance(clickTimes)
if (timeVariances < 0.1) {
patterns.push('Robotically precise click timing')
score -= 30
}
}
return { score: Math.max(0, score), patterns }
}
private analyzeScrollBehavior(): { score: number; patterns: string[] } {
const patterns: string[] = []
let score = 100
if (this.behaviorData.scrolls.length > 0) {
// Check for unnatural scroll patterns
const scrollVelocities = this.behaviorData.scrolls.map(s => s.velocity)
const avgVelocity = scrollVelocities.reduce((a, b) => a + b, 0) / scrollVelocities.length
// Too consistent scroll speed
if (this.calculateVariance(scrollVelocities) < 0.2) {
patterns.push('Unnaturally consistent scroll velocity')
score -= 25
}
}
return { score: Math.max(0, score), patterns }
}
private analyzeKeystrokeBehavior(): { score: number; patterns: string[] } {
const patterns: string[] = []
let score = 100
if (this.behaviorData.keystrokes.length > 0) {
const timeBetweenKeystrokes = this.behaviorData.keystrokes.map(k => k.timeBetweenKeystrokes)
// Too consistent typing speed
const avgTimeBetween = timeBetweenKeystrokes.reduce((a, b) => a + b, 0) / timeBetweenKeystrokes.length
const timeVariance = this.calculateVariance(timeBetweenKeystrokes)
if (timeVariance < 0.3 && avgTimeBetween < 200) {
patterns.push('Robotically consistent typing rhythm')
score -= 35
}
}
return { score: Math.max(0, score), patterns }
}
private analyzeSessionBehavior(): { score: number; patterns: string[] } {
const patterns: string[] = []
let score = 100
// Check session duration patterns
if (this.behaviorData.sessionDuration > 0) {
// Very short sessions with many actions suggest scraping
if (this.behaviorData.sessionDuration < 30000 && // Less than 30 seconds
this.behaviorData.clicks.length > 10) {
patterns.push('Suspiciously short session with high activity')
score -= 40
}
// Too many page views in short time
if (this.behaviorData.sessionDuration < 60000 && // Less than 1 minute
this.behaviorData.pageViews > 5) {
patterns.push('Unusual page view frequency')
score -= 30
}
}
return { score: Math.max(0, score), patterns }
}
private calculateVariance(values: number[]): number {
if (values.length === 0) return 0
const mean = values.reduce((a, b) => a + b, 0) / values.length
const squaredDiffs = values.map(value => Math.pow(value - mean, 2))
return squaredDiffs.reduce((a, b) => a + b, 0) / values.length
}
private calculateAverageTimeBetween(timestamps: number[]): number {
if (timestamps.length < 2) return 0
const diffs = []
for (let i = 1; i < timestamps.length; i++) {
diffs.push(timestamps[i] - timestamps[i - 1])
}
return diffs.reduce((a, b) => a + b, 0) / diffs.length
}
private calculateTimeVariance(timestamps: number[]): number {
const avgTime = this.calculateAverageTimeBetween(timestamps)
if (avgTime === 0) return 0
const diffs = []
for (let i = 1; i < timestamps.length; i++) {
diffs.push(Math.abs(timestamps[i] - timestamps[i - 1] - avgTime))
}
return diffs.reduce((a, b) => a + b, 0) / diffs.length / avgTime
}
}
// Usage example
const behaviorAnalyzer = new BehaviorAnalyzer()
// After 30 seconds of user interaction, analyze behavior
setTimeout(() => {
const score = behaviorAnalyzer.analyzeBehavior()
console.log('Behavior analysis:', score)
if (score.riskLevel === 'high') {
// Trigger additional verification or rate limiting
console.log('High risk behavior detected - implementing safeguards')
}
}, 30000)Practical Implementation Examples
Machine Learning Bot Detection
// Machine learning-based bot detection using TensorFlow.js
interface MLFeatures {
canvasFingerprint: string
webglFingerprint: string
fontCount: number
pluginCount: number
avgMouseVelocity: number
clickFrequency: number
scrollVariability: number
keystrokeConsistency: number
sessionDuration: number
pageViewRate: number
timeBetweenActions: number[]
requestRate: number
endpointDiversity: number
headerConsistency: number
}
class MLBotDetector {
async predictBot(features: MLFeatures): Promise<boolean> {
// Simplified ML prediction - in production use trained model
const botScore = this.calculateBotScore(features)
return botScore > 0.7
}
private calculateBotScore(features: MLFeatures): number {
let score = 0
// Browser fingerprinting score
if (features.fontCount < 10) score += 0.3
if (features.pluginCount === 0) score += 0.2
// Behavioral analysis score
if (features.avgMouseVelocity < 50) score += 0.3
if (features.clickFrequency > 8) score += 0.2
if (features.keystrokeConsistency > 0.9) score += 0.3
// Session analysis score
if (features.sessionDuration < 60 && features.pageViewRate > 3) score += 0.4
return Math.min(1, score)
}
}}
}
### Advanced Machine Learning Bot Detection System
// Production-ready machine learning system for bot detection
interface BotDetectionFeatures {
// Browser and device characteristics
userAgent: string
screenResolution: string
timezone: string
language: string
platform: string
cookieEnabled: boolean
doNotTrack?: string
// Hardware and performance indicators
hardwareConcurrency: number
deviceMemory: number
maxTouchPoints: number
// Canvas and WebGL fingerprints
canvasFingerprint: string
webglFingerprint: string
audioFingerprint?: string
// Behavioral patterns
mouseMovementEntropy: number
clickTimingVariance: number
keystrokeRhythmConsistency: number
scrollBehaviorPattern: string
// Network and timing characteristics
requestIntervalVariance: number
sessionDuration: number
pageViewRate: number
endpointDiversity: number
// Header consistency and patterns
headerEntropy: number
acceptLanguageConsistency: number
referrerPattern: string
// Advanced indicators
automationToolsDetected: string[]
suspiciousHeaders: string[]
unusualProtocols: string[]
}
interface MLModelPrediction {
isBot: boolean
confidence: number // 0-100
botType?: 'scraper' | 'spammer' | 'credential_stuffer' | 'click_fraud' | 'unknown'
riskLevel: 'low' | 'medium' | 'high' | 'critical'
featureImportance: Record
modelVersion: string
predictionTimestamp: number
}
interface TrainingDataset {
features: BotDetectionFeatures
label: boolean // true for bot, false for human
metadata: {
source: string
timestamp: number
userId?: string
ip?: string
verificationMethod?: string
}
}
class AdvancedMLBotDetector {
private model: any = null // In production: TensorFlow.js model
private featureExtractor: FeatureExtractor
private modelVersion: string = '1.0.0'
private predictionCache: Map
private trainingData: TrainingDataset[] = []
constructor() {
this.featureExtractor = new FeatureExtractor()
this.initializeModel()
this.loadTrainingData()
}
async predictBot(features: BotDetectionFeatures, useCache: boolean = true): Promise
// Check cache first
const cacheKey = this.generateCacheKey(features)
if (useCache && this.predictionCache.has(cacheKey)) {
const cached = this.predictionCache.get(cacheKey)!
if (Date.now() - cached.predictionTimestamp < 5 60 1000) { // 5 minutes cache
return cached
}
}
try {
// Extract normalized features for ML model
const normalizedFeatures = await this.featureExtractor.normalizeFeatures(features)
// Run prediction
const prediction = await this.runModelPrediction(normalizedFeatures)
// Cache result
this.predictionCache.set(cacheKey, prediction)
return prediction
} catch (error) {
console.error('ML prediction failed:', error)
// Fallback to rule-based detection
return this.fallbackPrediction(features)
}
}
private async initializeModel(): Promise
// In production, load pre-trained TensorFlow.js model
// For demo, we'll simulate model initialization
console.log('Initializing ML bot detection model...')
// Simulate model loading
await new Promise(resolve => setTimeout(resolve, 1000))
this.model = {
predict: async (features: number[]) => {
// Simplified prediction logic for demo
const botScore = this.calculateSimpleBotScore(features)
return {
isBot: botScore > 0.7,
confidence: Math.min(100, botScore * 100),
botType: this.classifyBotType(botScore, features),
riskLevel: this.calculateRiskLevel(botScore)
}
}
}
console.log('ML model initialized successfully')
}
private calculateSimpleBotScore(features: number[]): number {
// Simplified scoring algorithm
let score = 0
// Browser consistency indicators (lower is more suspicious)
if (features[0] < 0.3) score += 0.2 // Low canvas entropy
if (features[1] < 0.2) score += 0.3 // Low WebGL entropy
if (features[2] < 0.1) score += 0.2 // Very few fonts
// Behavioral indicators
if (features[3] < 0.2) score += 0.3 // Low mouse movement entropy
if (features[4] > 0.8) score += 0.2 // High click timing consistency
if (features[5] < 0.3) score += 0.2 // Short session duration
// Network indicators
if (features[6] < 0.2) score += 0.3 // Low request interval variance
if (features[7] > 0.8) score += 0.2 // High page view rate
return Math.min(1, Math.max(0, score))
}
private classifyBotType(score: number, features: number[]): MLModelPrediction['botType'] {
if (score > 0.9) {
if (features[5] < 0.2 && features[6] < 0.3) return 'credential_stuffer'
if (features[7] > 0.8 && features[8] > 0.7) return 'scraper'
if (features[9] > 0.6) return 'click_fraud'
return 'spammer'
}
return 'unknown'
}
private calculateRiskLevel(score: number): MLModelPrediction['riskLevel'] {
if (score > 0.9) return 'critical'
if (score > 0.7) return 'high'
if (score > 0.5) return 'medium'
return 'low'
}
private async runModelPrediction(features: number[]): Promise
if (!this.model) {
throw new Error('Model not initialized')
}
const rawPrediction = await this.model.predict(features)
return {
...rawPrediction,
featureImportance: this.calculateFeatureImportance(features),
modelVersion: this.modelVersion,
predictionTimestamp: Date.now()
}
}
private calculateFeatureImportance(features: number[]): Record
// Simplified feature importance calculation
const importance: Record
// Weight different feature categories
importance['browser_fingerprint'] = Math.abs(features[0] 0.3 + features[1] 0.2 + features[2] * 0.1)
importance['behavioral_patterns'] = Math.abs(features[3] 0.25 + features[4] 0.2 + features[5] * 0.15)
importance['network_characteristics'] = Math.abs(features[6] 0.2 + features[7] 0.15 + features[8] * 0.1)
importance['header_analysis'] = Math.abs(features[9] 0.1 + features[10] 0.05)
return importance
}
private fallbackPrediction(features: BotDetectionFeatures): MLModelPrediction {
// Rule-based fallback when ML fails
let score = 0
const reasons: string[] = []
// Browser fingerprint rules
if (features.fontCount < 10) {
score += 30
reasons.push('Limited font support')
}
if (features.pluginCount === 0) {
score += 20
reasons.push('No browser plugins detected')
}
// Behavioral rules
if (features.mouseMovementEntropy < 0.3) {
score += 25
reasons.push('Low mouse movement entropy')
}
if (features.keystrokeRhythmConsistency > 0.9) {
score += 20
reasons.push('Highly consistent typing rhythm')
}
// Session rules
if (features.sessionDuration < 60 && features.pageViewRate > 3) {
score += 30
reasons.push('Suspicious session pattern')
}
const confidence = Math.min(100, score)
return {
isBot: confidence > 60,
confidence,
botType: confidence > 80 ? 'unknown' : undefined,
riskLevel: confidence > 80 ? 'high' : confidence > 50 ? 'medium' : 'low',
featureImportance: {},
modelVersion: 'fallback-1.0',
predictionTimestamp: Date.now()
}
}
private generateCacheKey(features: BotDetectionFeatures): string {
// Create deterministic cache key from features
const keyData = ${features.canvasFingerprint}-${features.webglFingerprint}-${features.sessionDuration}-${features.mouseMovementEntropy}
return btoa(keyData).slice(0, 16) // Short hash for cache key
}
// Training data management
async addTrainingExample(features: BotDetectionFeatures, isBot: boolean, metadata: any): Promise
const dataset: TrainingDataset = {
features,
label: isBot,
metadata: {
source: 'manual_verification',
timestamp: Date.now(),
...metadata
}
}
this.trainingData.push(dataset)
// In production, save to persistent storage
await this.saveTrainingData()
}
private async loadTrainingData(): Promise
// In production, load from database or file system
console.log('Loading training data...')
// For demo, we'll use empty dataset
}
private async saveTrainingData(): Promise
// In production, save to database
console.log(Saved ${this.trainingData.length} training examples)
}
// Model retraining
async retrainModel(newData?: TrainingDataset[]): Promise
const dataToUse = newData || this.trainingData
if (dataToUse.length < 100) {
console.log('Insufficient training data for retraining')
return
}
console.log('Starting model retraining...')
// In production, implement actual model retraining
// For demo, simulate retraining
await new Promise(resolve => setTimeout(resolve, 5000))
this.modelVersion = 2.0.${Date.now()}
console.log('Model retraining completed')
}
// Performance monitoring
getModelPerformance(): {
totalPredictions: number
accuracy: number
precision: number
recall: number
f1Score: number
} {
// In production, calculate from validation data
return {
totalPredictions: this.predictionCache.size,
accuracy: 0.94,
precision: 0.91,
recall: 0.89,
f1Score: 0.90
}
}
}
class FeatureExtractor {
async normalizeFeatures(features: BotDetectionFeatures): Promise
// Normalize features to 0-1 range for ML model
return [
this.normalizeCanvasEntropy(features.canvasFingerprint),
this.normalizeWebGLEntropy(features.webglFingerprint),
this.normalizeFontCount(features.fontCount),
this.normalizeEntropy(features.mouseMovementEntropy),
this.normalizeConsistency(features.clickTimingVariance),
this.normalizeDuration(features.sessionDuration),
this.normalizeVariance(features.requestIntervalVariance),
this.normalizeRate(features.pageViewRate),
this.normalizeDiversity(features.endpointDiversity),
this.normalizeEntropy(features.headerEntropy),
this.normalizeConsistency(features.acceptLanguageConsistency)
]
}
private normalizeCanvasEntropy(fingerprint: string): number {
// Calculate entropy of canvas fingerprint
const entropy = this.calculateEntropy(fingerprint)
return Math.min(1, entropy / 4.5) // Normalize to 0-1
}
private normalizeWebGLEntropy(fingerprint: string): number {
const entropy = this.calculateEntropy(fingerprint)
return Math.min(1, entropy / 3.0)
}
private normalizeFontCount(count: number): number {
return Math.min(1, count / 200) // Normalize to typical range
}
private normalizeEntropy(entropy: number): number {
return Math.min(1, entropy / 8.0)
}
private normalizeConsistency(consistency: number): number {
return consistency // Already 0-1
}
private normalizeDuration(duration: number): number {
return Math.min(1, duration / 300) // Normalize to 5 minutes
}
private normalizeVariance(variance: number): number {
return Math.min(1, variance / 1000) // Normalize variance
}
private normalizeRate(rate: number): number {
return Math.min(1, rate / 10) // Normalize page views per minute
}
private normalizeDiversity(diversity: number): number {
return diversity // Already 0-1
}
private calculateEntropy(str: string): number {
const frequencies: Record
for (const char of str) {
frequencies[char] = (frequencies[char] || 0) + 1
}
let entropy = 0
const length = str.length
for (const count of Object.values(frequencies)) {
const probability = count / length
entropy -= probability * Math.log2(probability)
}
return entropy
}
}
// Integration with Express.js middleware
class BotDetectionMiddleware {
private mlDetector: AdvancedMLBotDetector
constructor() {
this.mlDetector = new AdvancedMLBotDetector()
}
async detectBot(req: any, res: any, next: any): Promise
try {
// Collect features from request
const features = await this.extractFeaturesFromRequest(req)
// Run ML prediction
const prediction = await this.mlDetector.predictBot(features)
// Add detection results to request
req.botDetection = {
prediction,
features,
timestamp: Date.now()
}
// Set response headers for client-side detection
res.set({
'X-Bot-Score': prediction.confidence.toString(),
'X-Bot-Type': prediction.botType || 'unknown',
'X-Risk-Level': prediction.riskLevel
})
// Apply mitigation based on risk level
if (prediction.riskLevel === 'critical') {
return res.status(429).json({
error: 'Request blocked',
message: 'Suspicious activity detected',
retryAfter: 300
})
}
if (prediction.riskLevel === 'high') {
// Require additional verification
req.requiresVerification = true
console.warn(High-risk request detected from IP: ${req.ip})
}
next()
} catch (error) {
console.error('Bot detection middleware error:', error)
// Continue without blocking on detection failure
next()
}
}
private async extractFeaturesFromRequest(req: any): Promise
return {
userAgent: req.get('User-Agent') || '',
screenResolution: req.get('X-Screen-Resolution') || 'unknown',
timezone: req.get('X-Timezone') || 'unknown',
language: req.get('Accept-Language')?.split(',')[0] || 'unknown',
platform: req.get('X-Platform') || 'unknown',
cookieEnabled: req.get('X-Cookie-Enabled') === 'true',
doNotTrack: req.get('DNT'),
hardwareConcurrency: parseInt(req.get('X-Hardware-Concurrency')) || 0,
deviceMemory: parseFloat(req.get('X-Device-Memory')) || 0,
maxTouchPoints: parseInt(req.get('X-Max-Touch-Points')) || 0,
canvasFingerprint: req.get('X-Canvas-Fingerprint') || '',
webglFingerprint: req.get('X-WebGL-Fingerprint') || '',
mouseMovementEntropy: parseFloat(req.get('X-Mouse-Entropy')) || 0,
clickTimingVariance: parseFloat(req.get('X-Click-Variance')) || 0,
keystrokeRhythmConsistency: parseFloat(req.get('X-Keystroke-Consistency')) || 0,
scrollBehaviorPattern: req.get('X-Scroll-Pattern') || 'unknown',
requestIntervalVariance: parseFloat(req.get('X-Request-Variance')) || 0,
sessionDuration: parseFloat(req.get('X-Session-Duration')) || 0,
pageViewRate: parseFloat(req.get('X-Page-View-Rate')) || 0,
endpointDiversity: parseFloat(req.get('X-Endpoint-Diversity')) || 0,
headerEntropy: parseFloat(req.get('X-Header-Entropy')) || 0,
acceptLanguageConsistency: parseFloat(req.get('X-Language-Consistency')) || 0,
referrerPattern: req.get('X-Referrer-Pattern') || 'unknown',
automationToolsDetected: (req.get('X-Automation-Tools') || '').split(','),
suspiciousHeaders: (req.get('X-Suspicious-Headers') || '').split(','),
unusualProtocols: (req.get('X-Unusual-Protocols') || '').split(',')
}
}
}
// Initialize bot detection system
const botDetectionMiddleware = new BotDetectionMiddleware()
// Apply to API routes
app.use('/api', botDetectionMiddleware.detectBot)
// Bot detection analysis endpoint
app.post('/api/analyze-bot-risk', async (req, res) => {
try {
const { features, useCache } = req.body
if (!features) {
return res.status(400).json({ error: 'Features required' })
}
const prediction = await botDetectionMiddleware['mlDetector'].predictBot(features, useCache)
res.json({
prediction,
features,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Bot analysis error:', error)
res.status(500).json({ error: 'Analysis failed' })
}
})
// Model performance endpoint
app.get('/api/bot-detection/performance', (req, res) => {
const performance = botDetectionMiddleware['mlDetector'].getModelPerformance()
res.json({
performance,
modelVersion: botDetectionMiddleware['mlDetector']['modelVersion'],
timestamp: new Date().toISOString()
})
})
// Training data submission endpoint
app.post('/api/bot-detection/training', async (req, res) => {
try {
const { features, isBot, metadata } = req.body
if (!features || typeof isBot !== 'boolean') {
return res.status(400).json({ error: 'Features and isBot label required' })
}
await botDetectionMiddleware['mlDetector'].addTrainingExample(features, isBot, metadata)
res.json({
message: 'Training example added successfully',
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Training data submission error:', error)
res.status(500).json({ error: 'Training data submission failed' })
}
})
// Manual model retraining trigger
app.post('/api/bot-detection/retrain', async (req, res) => {
try {
await botDetectionMiddleware['mlDetector'].retrainModel()
res.json({
message: 'Model retraining completed',
newVersion: botDetectionMiddleware['mlDetector']['modelVersion'],
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Model retraining error:', error)
res.status(500).json({ error: 'Model retraining failed' })
}
})
// Clear prediction cache
app.post('/api/bot-detection/clear-cache', (req, res) => {
botDetectionMiddleware['mlDetector']['predictionCache'].clear()
res.json({
message: 'Prediction cache cleared',
timestamp: new Date().toISOString()
})
})
console.log('Advanced ML bot detection system initialized')
### Real-Time Traffic Analysis Engine
// Real-time traffic analysis for bot detection and behavioral insights
interface TrafficEvent {
id: string
timestamp: number
ip: string
userAgent: string
endpoint: string
method: string
statusCode: number
responseTime: number
requestSize: number
responseSize: number
headers: Record
fingerprint?: string
sessionId?: string
userId?: string
}
interface TrafficPattern {
id: string
patternType: 'burst' | 'periodic' | 'sequential' | 'random' | 'suspicious'
confidence: number
characteristics: {
requestRate: number // requests per minute
endpointDistribution: Record
timeWindow: number // minutes
uniqueIPs: number
avgResponseTime: number
errorRate: number
}
affectedIPs: string[]
firstSeen: number
lastSeen: number
riskScore: number
recommendations: string[]
}
interface BehavioralProfile {
ip: string
sessionId?: string
userAgent: string
fingerprint?: string
behaviorMetrics: {
avgRequestInterval: number
endpointDiversity: number
errorRate: number
successRate: number
sessionDuration: number
pageViews: number
timeBetweenActions: number[]
}
patternHistory: string[]
riskScore: number
lastUpdated: number
}
class RealTimeTrafficAnalyzer {
private eventBuffer: TrafficEvent[] = []
private activePatterns: Map
private behavioralProfiles: Map
private patternDetectors: PatternDetector[] = []
private maxBufferSize: number = 10000
constructor() {
this.initializePatternDetectors()
this.startRealTimeAnalysis()
}
async analyzeTrafficEvent(event: TrafficEvent): Promise<{
patterns: TrafficPattern[]
behavioralUpdate: boolean
riskAssessment: {
level: 'low' | 'medium' | 'high' | 'critical'
score: number
reasons: string[]
}
recommendations: string[]
}> {
// Add event to buffer
this.addToBuffer(event)
// Update behavioral profile
const behavioralUpdate = await this.updateBehavioralProfile(event)
// Detect patterns
const patterns = await this.detectPatterns(event)
// Assess risk
const riskAssessment = this.assessRisk(event, patterns, behavioralUpdate)
// Generate recommendations
const recommendations = this.generateRecommendations(riskAssessment, patterns)
return {
patterns,
behavioralUpdate,
riskAssessment,
recommendations
}
}
private addToBuffer(event: TrafficEvent): void {
this.eventBuffer.push(event)
// Maintain buffer size
if (this.eventBuffer.length > this.maxBufferSize) {
this.eventBuffer = this.eventBuffer.slice(-this.maxBufferSize)
}
// Clean old events (older than 1 hour)
const oneHourAgo = Date.now() - 60 60 1000
this.eventBuffer = this.eventBuffer.filter(e => e.timestamp > oneHourAgo)
}
private async updateBehavioralProfile(event: TrafficEvent): Promise
const profileKey = event.sessionId || event.ip
let profile = this.behavioralProfiles.get(profileKey)
if (!profile) {
profile = {
ip: event.ip,
sessionId: event.sessionId,
userAgent: event.userAgent,
fingerprint: event.fingerprint,
behaviorMetrics: {
avgRequestInterval: 0,
endpointDiversity: 0,
errorRate: 0,
successRate: 0,
sessionDuration: 0,
pageViews: 0,
timeBetweenActions: []
},
patternHistory: [],
riskScore: 0,
lastUpdated: Date.now()
}
this.behavioralProfiles.set(profileKey, profile)
}
// Update metrics
await this.updateProfileMetrics(profile, event)
// Calculate new risk score
const oldRiskScore = profile.riskScore
profile.riskScore = this.calculateProfileRiskScore(profile)
// Check if risk level changed significantly
const riskChanged = Math.abs(profile.riskScore - oldRiskScore) > 20
profile.lastUpdated = Date.now()
this.behavioralProfiles.set(profileKey, profile)
return riskChanged
}
private async updateProfileMetrics(profile: BehavioralProfile, event: TrafficEvent): Promise
const now = Date.now()
// Update request timing
if (profile.behaviorMetrics.timeBetweenActions.length > 0) {
const lastAction = Math.max(...profile.behaviorMetrics.timeBetweenActions)
const timeSinceLastAction = now - lastAction
if (timeSinceLastAction > 1000) { // Only track significant intervals
profile.behaviorMetrics.timeBetweenActions.push(now)
}
} else {
profile.behaviorMetrics.timeBetweenActions.push(now)
}
// Keep only recent intervals (last 10 minutes)
const tenMinutesAgo = now - 10 60 1000
profile.behaviorMetrics.timeBetweenActions = profile.behaviorMetrics.timeBetweenActions.filter(
t => t > tenMinutesAgo
)
// Update average request interval
if (profile.behaviorMetrics.timeBetweenActions.length > 1) {
const intervals = []
for (let i = 1; i < profile.behaviorMetrics.timeBetweenActions.length; i++) {
intervals.push(
profile.behaviorMetrics.timeBetweenActions[i] - profile.behaviorMetrics.timeBetweenActions[i - 1]
)
}
profile.behaviorMetrics.avgRequestInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length
}
// Update endpoint diversity
profile.behaviorMetrics.endpointDiversity = this.calculateEndpointDiversity(profileKey)
// Update error rate
if (event.statusCode >= 400) {
profile.behaviorMetrics.errorRate = this.updateErrorRate(profile, true)
} else {
profile.behaviorMetrics.errorRate = this.updateErrorRate(profile, false)
}
// Update success rate
if (event.statusCode < 400) {
profile.behaviorMetrics.successRate = this.updateSuccessRate(profile, true)
} else {
profile.behaviorMetrics.successRate = this.updateSuccessRate(profile, false)
}
// Update session duration
if (profile.behaviorMetrics.timeBetweenActions.length > 0) {
const firstAction = Math.min(...profile.behaviorMetrics.timeBetweenActions)
profile.behaviorMetrics.sessionDuration = now - firstAction
}
// Update page views
profile.behaviorMetrics.pageViews++
}
private calculateEndpointDiversity(profileKey: string): number {
// Get recent events for this profile
const recentEvents = this.eventBuffer.filter(e => {
const key = e.sessionId || e.ip
return key === profileKey && e.timestamp > Date.now() - 10 60 1000
})
const endpoints = new Set(recentEvents.map(e => e.endpoint))
const totalRequests = recentEvents.length
// Calculate diversity as unique endpoints / total requests
return totalRequests > 0 ? endpoints.size / totalRequests : 0
}
private updateErrorRate(profile: BehavioralProfile, isError: boolean): number {
// Simple exponential moving average for error rate
const alpha = 0.1
const currentRate = profile.behaviorMetrics.errorRate
if (isError) {
return currentRate (1 - alpha) + alpha 1
} else {
return currentRate * (1 - alpha)
}
}
private updateSuccessRate(profile: BehavioralProfile, isSuccess: boolean): number {
const alpha = 0.1
const currentRate = profile.behaviorMetrics.successRate
if (isSuccess) {
return currentRate (1 - alpha) + alpha 1
} else {
return currentRate * (1 - alpha)
}
}
private calculateProfileRiskScore(profile: BehavioralProfile): number {
let score = 0
// High error rate suggests bot activity
if (profile.behaviorMetrics.errorRate > 0.3) {
score += 30
}
// Very low request interval suggests automation
if (profile.behaviorMetrics.avgRequestInterval < 100) {
score += 25
}
// Low endpoint diversity suggests scraping
if (profile.behaviorMetrics.endpointDiversity < 0.2) {
score += 20
}
// Very short session with many requests suggests bot
if (profile.behaviorMetrics.sessionDuration < 60000 && profile.behaviorMetrics.pageViews > 10) {
score += 25
}
// Consistent timing patterns suggest automation
if (profile.behaviorMetrics.timeBetweenActions.length > 5) {
const variance = this.calculateVariance(profile.behaviorMetrics.timeBetweenActions)
if (variance < 0.2) {
score += 15
}
}
return Math.min(100, score)
}
private calculateVariance(values: number[]): number {
if (values.length === 0) return 0
const mean = values.reduce((a, b) => a + b, 0) / values.length
const squaredDiffs = values.map(value => Math.pow(value - mean, 2))
return squaredDiffs.reduce((a, b) => a + b, 0) / values.length
}
private async detectPatterns(event: TrafficEvent): Promise
const newPatterns: TrafficPattern[] = []
for (const detector of this.patternDetectors) {
const pattern = await detector.detect(event, this.eventBuffer)
if (pattern) {
newPatterns.push(pattern)
this.activePatterns.set(pattern.id, pattern)
}
}
return newPatterns
}
private initializePatternDetectors(): void {
this.patternDetectors = [
new BurstPatternDetector(),
new PeriodicPatternDetector(),
new SequentialPatternDetector(),
new SuspiciousPatternDetector()
]
}
private assessRisk(
event: TrafficEvent,
patterns: TrafficPattern[],
behavioralUpdate: boolean
): { level: 'low' | 'medium' | 'high' | 'critical'; score: number; reasons: string[] } {
let score = 0
const reasons: string[] = []
// Check for active suspicious patterns
const suspiciousPatterns = patterns.filter(p => p.riskScore > 70)
if (suspiciousPatterns.length > 0) {
score += 40
reasons.push(${suspiciousPatterns.length} suspicious traffic patterns detected)
}
// Check behavioral profile risk
const profileKey = event.sessionId || event.ip
const profile = this.behavioralProfiles.get(profileKey)
if (profile && profile.riskScore > 60) {
score += profile.riskScore * 0.3
reasons.push('High-risk behavioral profile')
}
// Check request characteristics
if (event.responseTime < 50 && event.requestSize < 100) {
score += 10
reasons.push('Unusually fast request')
}
if (event.statusCode >= 500) {
score += 15
reasons.push('Server error response')
}
// Check for automation indicators in headers
const suspiciousHeaders = this.detectSuspiciousHeaders(event.headers)
if (suspiciousHeaders.length > 0) {
score += 20
reasons.push('Suspicious request headers')
}
const finalScore = Math.min(100, score)
let level: 'low' | 'medium' | 'high' | 'critical'
if (finalScore > 80) level = 'critical'
else if (finalScore > 60) level = 'high'
else if (finalScore > 40) level = 'medium'
else level = 'low'
return { level, score: finalScore, reasons }
}
private detectSuspiciousHeaders(headers: Record
const suspicious: string[] = []
// Check for automation tool headers
const automationHeaders = [
'x-automation-tool',
'x-scraper',
'x-bot',
'x-crawler',
'user-agent-automation'
]
for (const header of automationHeaders) {
if (headers[header.toLowerCase()]) {
suspicious.push(header)
}
}
// Check for unusual header patterns
if (headers['accept-language']?.includes('*')) {
suspicious.push('wildcard-accept-language')
}
if (headers['cache-control']?.includes('no-cache') && headers['pragma']?.includes('no-cache')) {
suspicious.push('aggressive-cache-control')
}
return suspicious
}
private generateRecommendations(
riskAssessment: any,
patterns: TrafficPattern[]
): string[] {
const recommendations: string[] = []
if (riskAssessment.level === 'critical') {
recommendations.push('Block IP address immediately')
recommendations.push('Require CAPTCHA verification for session')
recommendations.push('Log incident for security team review')
}
if (riskAssessment.level === 'high') {
recommendations.push('Implement rate limiting')
recommendations.push('Require additional authentication')
recommendations.push('Monitor session closely')
}
// Pattern-specific recommendations
for (const pattern of patterns) {
if (pattern.patternType === 'burst') {
recommendations.push('Implement burst detection rate limiting')
}
if (pattern.patternType === 'periodic') {
recommendations.push('Add timing-based access controls')
}
if (pattern.patternType === 'suspicious') {
recommendations.push('Require manual review of affected IPs')
}
}
return recommendations
}
private startRealTimeAnalysis(): void {
// Process events every 10 seconds
setInterval(async () => {
await this.processEventBuffer()
}, 10000)
// Clean up old patterns every 5 minutes
setInterval(() => {
this.cleanupOldPatterns()
}, 5 60 1000)
// Clean up old profiles every hour
setInterval(() => {
this.cleanupOldProfiles()
}, 60 60 1000)
}
private async processEventBuffer(): Promise
if (this.eventBuffer.length === 0) return
// Process recent events for pattern detection
const recentEvents = this.eventBuffer.slice(-1000) // Last 1000 events
for (const event of recentEvents) {
await this.analyzeTrafficEvent(event)
}
}
private cleanupOldPatterns(): void {
const oneHourAgo = Date.now() - 60 60 1000
for (const [id, pattern] of this.activePatterns.entries()) {
if (pattern.lastSeen < oneHourAgo) {
this.activePatterns.delete(id)
}
}
}
private cleanupOldProfiles(): void {
const oneHourAgo = Date.now() - 60 60 1000
for (const [key, profile] of this.behavioralProfiles.entries()) {
if (profile.lastUpdated < oneHourAgo) {
this.behavioralProfiles.delete(key)
}
}
}
getActivePatterns(): TrafficPattern[] {
return Array.from(this.activePatterns.values())
}
getBehavioralProfile(key: string): BehavioralProfile | null {
return this.behavioralProfiles.get(key) || null
}
getTrafficAnalytics(): {
totalEvents: number
activePatterns: number
highRiskProfiles: number
averageRiskScore: number
} {
const profiles = Array.from(this.behavioralProfiles.values())
const highRiskProfiles = profiles.filter(p => p.riskScore > 60).length
const averageRiskScore = profiles.length > 0
? profiles.reduce((sum, p) => sum + p.riskScore, 0) / profiles.length
: 0
return {
totalEvents: this.eventBuffer.length,
activePatterns: this.activePatterns.size,
highRiskProfiles,
averageRiskScore
}
}
}
// Pattern detector base class
abstract class PatternDetector {
abstract detect(event: TrafficEvent, buffer: TrafficEvent[]): Promise
}
class BurstPatternDetector extends PatternDetector {
async detect(event: TrafficEvent, buffer: TrafficEvent[]): Promise
// Detect burst patterns (many requests from same IP in short time)
const ipEvents = buffer.filter(e => e.ip === event.ip)
const recentIPEvents = ipEvents.filter(e => e.timestamp > Date.now() - 5 60 1000) // Last 5 minutes
if (recentIPEvents.length > 50) { // More than 50 requests in 5 minutes
return {
id: burst-${event.ip}-${Date.now()},
patternType: 'burst',
confidence: Math.min(100, (recentIPEvents.length / 50) * 50),
characteristics: {
requestRate: recentIPEvents.length / 5, // per minute
endpointDistribution: this.calculateEndpointDistribution(recentIPEvents),
timeWindow: 5,
uniqueIPs: 1,
avgResponseTime: recentIPEvents.reduce((sum, e) => sum + e.responseTime, 0) / recentIPEvents.length,
errorRate: recentIPEvents.filter(e => e.statusCode >= 400).length / recentIPEvents.length
},
affectedIPs: [event.ip],
firstSeen: Math.min(...recentIPEvents.map(e => e.timestamp)),
lastSeen: Math.max(...recentIPEvents.map(e => e.timestamp)),
riskScore: Math.min(100, recentIPEvents.length * 2),
recommendations: [
'Implement IP-based rate limiting',
'Require CAPTCHA verification',
'Monitor for distributed attacks'
]
}
}
return null
}
private calculateEndpointDistribution(events: TrafficEvent[]): Record
const distribution: Record
for (const event of events) {
distribution[event.endpoint] = (distribution[event.endpoint] || 0) + 1
}
// Convert to percentages
const total = events.length
for (const endpoint in distribution) {
distribution[endpoint] = distribution[endpoint] / total
}
return distribution
}
}
class PeriodicPatternDetector extends PatternDetector {
async detect(event: TrafficEvent, buffer: TrafficEvent[]): Promise
// Detect periodic patterns (regular intervals between requests)
const ipEvents = buffer.filter(e => e.ip === event.ip)
const recentIPEvents = ipEvents.filter(e => e.timestamp > Date.now() - 10 60 1000) // Last 10 minutes
if (recentIPEvents.length > 10) {
const intervals = []
for (let i = 1; i < recentIPEvents.length; i++) {
intervals.push(recentIPEvents[i].timestamp - recentIPEvents[i - 1].timestamp)
}
// Check for periodic pattern (low variance in intervals)
const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length
const variance = this.calculateVariance(intervals)
if (avgInterval > 5000 && variance / avgInterval < 0.3) { // Periodic with low variance
return {
id: periodic-${event.ip}-${Date.now()},
patternType: 'periodic',
confidence: Math.min(100, (1 - variance / avgInterval) * 100),
characteristics: {
requestRate: 60 / (avgInterval / 1000), // per minute
endpointDistribution: this.calculateEndpointDistribution(recentIPEvents),
timeWindow: 10,
uniqueIPs: 1,
avgResponseTime: recentIPEvents.reduce((sum, e) => sum + e.responseTime, 0) / recentIPEvents.length,
errorRate: recentIPEvents.filter(e => e.statusCode >= 400).length / recentIPEvents.length
},
affectedIPs: [event.ip],
firstSeen: Math.min(...recentIPEvents.map(e => e.timestamp)),
lastSeen: Math.max(...recentIPEvents.map(e => e.timestamp)),
riskScore: Math.min(100, (1 - variance / avgInterval) * 80),
recommendations: [
'Implement time-based access controls',
'Add random delays to prevent timing attacks',
'Monitor for coordinated bot networks'
]
}
}
}
return null
}
private calculateVariance(values: number[]): number {
if (values.length === 0) return 0
const mean = values.reduce((a, b) => a + b, 0) / values.length
const squaredDiffs = values.map(value => Math.pow(value - mean, 2))
return squaredDiffs.reduce((a, b) => a + b, 0) / values.length
}
private calculateEndpointDistribution(events: TrafficEvent[]): Record
const distribution: Record
for (const event of events) {
distribution[event.endpoint] = (distribution[event.endpoint] || 0) + 1
}
const total = events.length
for (const endpoint in distribution) {
distribution[endpoint] = distribution[endpoint] / total
}
return distribution
}
}
class SequentialPatternDetector extends PatternDetector {
async detect(event: TrafficEvent, buffer: TrafficEvent[]): Promise
// Detect sequential patterns (predictable endpoint access patterns)
const recentEvents = buffer.filter(e => e.timestamp > Date.now() - 5 60 1000)
if (recentEvents.length > 20) {
const endpointSequence = recentEvents.map(e => e.endpoint)
const uniqueEndpoints = [...new Set(endpointSequence)]
// Check for repetitive sequential patterns
if (uniqueEndpoints.length < 5 && this.detectRepetitivePattern(endpointSequence)) {
return {
id: sequential-${Date.now()},
patternType: 'sequential',
confidence: 75,
characteristics: {
requestRate: recentEvents.length / 5,
endpointDistribution: this.calculateEndpointDistribution(recentEvents),
timeWindow: 5,
uniqueIPs: new Set(recentEvents.map(e => e.ip)).size,
avgResponseTime: recentEvents.reduce((sum, e) => sum + e.responseTime, 0) / recentEvents.length,
errorRate: recentEvents.filter(e => e.statusCode >= 400).length / recentEvents.length
},
affectedIPs: [...new Set(recentEvents.map(e => e.ip))],
firstSeen: Math.min(...recentEvents.map(e => e.timestamp)),
lastSeen: Math.max(...recentEvents.map(e => e.timestamp)),
riskScore: 60,
recommendations: [
'Implement endpoint access pattern detection',
'Add sequence-based rate limiting',
'Monitor for data scraping activities'
]
}
}
}
return null
}
private detectRepetitivePattern(sequence: string[]): boolean {
if (sequence.length < 10) return false
// Check for repeating patterns (e.g., A-B-C-A-B-C)
for (let patternLength = 3; patternLength <= 6; patternLength++) {
const firstPart = sequence.slice(0, patternLength)
const secondPart = sequence.slice(patternLength, patternLength * 2)
if (firstPart.every((item, index) => item === secondPart[index])) {
return true
}
}
return false
}
private calculateEndpointDistribution(events: TrafficEvent[]): Record
const distribution: Record
for (const event of events) {
distribution[event.endpoint] = (distribution[event.endpoint] || 0) + 1
}
const total = events.length
for (const endpoint in distribution) {
distribution[endpoint] = distribution[endpoint] / total
}
return distribution
}
}
class SuspiciousPatternDetector extends PatternDetector {
async detect(event: TrafficEvent, buffer: TrafficEvent[]): Promise
// Detect various suspicious patterns
let riskScore = 0
const reasons: string[] = []
// Check for suspicious user agents
const suspiciousUserAgents = ['bot', 'crawler', 'scraper', 'spider', 'automation']
if (suspiciousUserAgents.some(term => event.userAgent.toLowerCase().includes(term))) {
riskScore += 40
reasons.push('Suspicious user agent')
}
// Check for unusual request patterns
if (event.requestSize > 10000 && event.responseSize < 1000) {
riskScore += 30
reasons.push('Unusual request/response size ratio')
}
// Check for rapid endpoint switching
const ipEvents = buffer.filter(e => e.ip === event.ip)
const recentIPEvents = ipEvents.filter(e => e.timestamp > Date.now() - 2 60 1000)
if (recentIPEvents.length > 20) {
const uniqueEndpoints = new Set(recentIPEvents.map(e => e.endpoint))
const endpointSwitchRate = uniqueEndpoints.size / recentIPEvents.length
if (endpointSwitchRate > 0.8) {
riskScore += 25
reasons.push('Rapid endpoint switching')
}
}
// Check for consistent error patterns
const errorEvents = recentIPEvents.filter(e => e.statusCode >= 400)
if (errorEvents.length > 10 && errorEvents.length / recentIPEvents.length > 0.5) {
riskScore += 20
reasons.push('High error rate pattern')
}
if (riskScore > 60) {
return {
id: suspicious-${event.ip}-${Date.now()},
patternType: 'suspicious',
confidence: Math.min(100, riskScore),
characteristics: {
requestRate: recentIPEvents.length / 2,
endpointDistribution: this.calculateEndpointDistribution(recentIPEvents),
timeWindow: 2,
uniqueIPs: 1,
avgResponseTime: recentIPEvents.reduce((sum, e) => sum + e.responseTime, 0) / recentIPEvents.length,
errorRate: errorEvents.length / recentIPEvents.length
},
affectedIPs: [event.ip],
firstSeen: Math.min(...recentIPEvents.map(e => e.timestamp)),
lastSeen: Math.max(...recentIPEvents.map(e => e.timestamp)),
riskScore,
recommendations: [
'Require manual verification',
'Implement stricter rate limiting',
'Log for security team review',
'Consider IP blocking'
]
}
}
return null
}
private calculateEndpointDistribution(events: TrafficEvent[]): Record
const distribution: Record
for (const event of events) {
distribution[event.endpoint] = (distribution[event.endpoint] || 0) + 1
}
const total = events.length
for (const endpoint in distribution) {
distribution[endpoint] = distribution[endpoint] / total
}
return distribution
}
}
// Initialize traffic analyzer
const trafficAnalyzer = new RealTimeTrafficAnalyzer()
// Traffic analysis middleware
const trafficAnalysisMiddleware = async (req: any, res: any, next: any) => {
const startTime = Date.now()
// Capture response data
const originalSend = res.send
res.send = function(data: any) {
const responseTime = Date.now() - startTime
// Create traffic event
const event: TrafficEvent = {
id: event-${Date.now()}-${Math.random().toString(36).substr(2, 9)},
timestamp: Date.now(),
ip: req.ip || req.connection.remoteAddress || 'unknown',
userAgent: req.get('User-Agent') || '',
endpoint: req.path,
method: req.method,
statusCode: res.statusCode,
responseTime,
requestSize: JSON.stringify(req.body || {}).length,
responseSize: JSON.stringify(data || {}).length,
headers: this.getHeaders(),
sessionId: req.sessionID,
fingerprint: req.get('X-Fingerprint')
}
// Analyze traffic asynchronously
trafficAnalyzer.analyzeTrafficEvent(event).catch(error => {
console.error('Traffic analysis error:', error)
})
// Call original send
originalSend.call(this, data)
}
next()
}
// Apply traffic analysis middleware
app.use('/api', trafficAnalysisMiddleware)
// Traffic analytics endpoint
app.get('/api/traffic-analytics', (req, res) => {
const analytics = trafficAnalyzer.getTrafficAnalytics()
const patterns = trafficAnalyzer.getActivePatterns()
res.json({
analytics,
patterns: patterns.slice(0, 10), // Return top 10 patterns
timestamp: new Date().toISOString()
})
})
// Behavioral profile endpoint
app.get('/api/behavioral-profile/:key', (req, res) => {
const { key } = req.params
const profile = trafficAnalyzer.getBehavioralProfile(key)
if (!profile) {
return res.status(404).json({ error: 'Profile not found' })
}
res.json({
profile,
timestamp: new Date().toISOString()
})
})
// Real-time traffic monitoring (WebSocket)
app.ws('/api/traffic-monitor', (ws: any) => {
console.log('Traffic monitoring client connected')
const sendUpdate = () => {
const analytics = trafficAnalyzer.getTrafficAnalytics()
const patterns = trafficAnalyzer.getActivePatterns()
ws.send(JSON.stringify({
type: 'traffic_update',
analytics,
patterns: patterns.slice(0, 5), // Send only recent patterns
timestamp: new Date().toISOString()
}))
}
// Send updates every 10 seconds
const interval = setInterval(sendUpdate, 10000)
ws.on('close', () => {
console.log('Traffic monitoring client disconnected')
clearInterval(interval)
})
})
console.log('Real-time traffic analysis engine initialized')
console.log('Real-time traffic analysis engine initialized')Advanced Mitigation Strategies
// Comprehensive bot mitigation and response system
interface MitigationStrategy {
id: string
name: string
type: 'rate_limiting' | 'challenge' | 'block' | 'redirect' | 'delay'
conditions: {
riskLevel: string[]
botTypes: string[]
trafficPatterns: string[]
requestVolume: number
}
actions: {
rateLimit?: {
requestsPerMinute: number
burstLimit: number
windowSize: number
}
challenge?: {
type: 'captcha' | 'proof_of_work' | 'device_fingerprint' | 'behavioral'
difficulty: 'low' | 'medium' | 'high'
timeout: number
}
block?: {
duration: number // minutes
reason: string
}
redirect?: {
url: string
preserveQuery: boolean
}
delay?: {
milliseconds: number
jitter: boolean
}
}
priority: number
enabled: boolean
}
interface MitigationAction {
strategyId: string
actionType: string
timestamp: number
ip: string
sessionId?: string
reason: string
duration?: number
metadata: Record<string, any>
}
class AdvancedMitigationEngine {
private strategies: Map<string, MitigationStrategy> = new Map()
private activeMitigations: Map<string, MitigationAction[]> = new Map()
private mitigationStats: Map<string, { triggered: number; effective: number; falsePositive: number }> = new Map()
constructor() {
this.initializeDefaultStrategies()
this.startMitigationMonitoring()
}
async applyMitigation(
riskAssessment: any,
trafficEvent: any,
patterns: any[]
): Promise<{
actions: MitigationAction[]
blocked: boolean
requiresVerification: boolean
response: any
}> {
const applicableStrategies = this.findApplicableStrategies(riskAssessment, patterns)
const actions: MitigationAction[] = []
for (const strategy of applicableStrategies) {
const action = await this.executeStrategy(strategy, riskAssessment, trafficEvent)
if (action) {
actions.push(action)
}
}
// Determine if request should be blocked
const blockingActions = actions.filter(a => a.actionType === 'block')
const blocked = blockingActions.length > 0
// Check if verification is required
const challengeActions = actions.filter(a => a.actionType === 'challenge')
const requiresVerification = challengeActions.length > 0
// Generate response based on actions
const response = this.generateMitigationResponse(actions, blocked, requiresVerification)
// Record mitigation for analysis
this.recordMitigationActions(actions, trafficEvent.ip)
return {
actions,
blocked,
requiresVerification,
response
}
}
private findApplicableStrategies(riskAssessment: any, patterns: any[]): MitigationStrategy[] {
const applicable: MitigationStrategy[] = []
for (const strategy of this.strategies.values()) {
if (!strategy.enabled) continue
// Check risk level condition
if (strategy.conditions.riskLevel.length > 0 &&
!strategy.conditions.riskLevel.includes(riskAssessment.level)) {
continue
}
// Check bot type condition
if (riskAssessment.botType && strategy.conditions.botTypes.length > 0 &&
!strategy.conditions.botTypes.includes(riskAssessment.botType)) {
continue
}
// Check traffic pattern condition
if (strategy.conditions.trafficPatterns.length > 0) {
const hasMatchingPattern = patterns.some(p =>
strategy.conditions.trafficPatterns.includes(p.patternType)
)
if (!hasMatchingPattern) continue
}
applicable.push(strategy)
}
// Sort by priority
return applicable.sort((a, b) => b.priority - a.priority)
}
private async executeStrategy(
strategy: MitigationStrategy,
riskAssessment: any,
trafficEvent: any
): Promise<MitigationAction | null> {
const action: MitigationAction = {
strategyId: strategy.id,
actionType: strategy.type,
timestamp: Date.now(),
ip: trafficEvent.ip,
sessionId: trafficEvent.sessionId,
reason: `Applied ${strategy.name} due to ${riskAssessment.level} risk`,
metadata: {
riskLevel: riskAssessment.level,
botType: riskAssessment.botType,
confidence: riskAssessment.confidence
}
}
switch (strategy.type) {
case 'rate_limiting':
return await this.applyRateLimiting(action, strategy)
case 'challenge':
return await this.applyChallenge(action, strategy)
case 'block':
return await this.applyBlocking(action, strategy)
case 'redirect':
return await this.applyRedirect(action, strategy)
case 'delay':
return await this.applyDelay(action, strategy)
default:
return null
}
}
private async applyRateLimiting(action: MitigationAction, strategy: MitigationStrategy): Promise<MitigationAction> {
action.duration = strategy.actions.rateLimit?.windowSize || 60
action.metadata.rateLimit = strategy.actions.rateLimit
// In production, integrate with rate limiting service
console.log(`Applied rate limiting for IP ${action.ip}: ${JSON.stringify(strategy.actions.rateLimit)}`)
return action
}
private async applyChallenge(action: MitigationAction, strategy: MitigationStrategy): Promise<MitigationAction> {
action.metadata.challenge = strategy.actions.challenge
// In production, integrate with CAPTCHA or PoW service
console.log(`Applied challenge for IP ${action.ip}: ${JSON.stringify(strategy.actions.challenge)}`)
return action
}
private async applyBlocking(action: MitigationAction, strategy: MitigationStrategy): Promise<MitigationAction> {
action.duration = strategy.actions.block?.duration || 60
action.reason = strategy.actions.block?.reason || action.reason
action.metadata.block = strategy.actions.block
// In production, add IP to blocklist
console.log(`Blocked IP ${action.ip} for ${action.duration} minutes: ${action.reason}`)
return action
}
private async applyRedirect(action: MitigationAction, strategy: MitigationStrategy): Promise<MitigationAction> {
action.metadata.redirect = strategy.actions.redirect
// In production, handle redirect logic
console.log(`Redirecting IP ${action.ip} to ${strategy.actions.redirect?.url}`)
return action
}
private async applyDelay(action: MitigationAction, strategy: MitigationStrategy): Promise<MitigationAction> {
action.metadata.delay = strategy.actions.delay
// In production, add artificial delay
console.log(`Adding delay for IP ${action.ip}: ${strategy.actions.delay?.milliseconds}ms`)
return action
}
private generateMitigationResponse(
actions: MitigationAction[],
blocked: boolean,
requiresVerification: boolean
): any {
if (blocked) {
return {
status: 429,
error: 'Request blocked',
message: 'Your request has been blocked due to suspicious activity',
retryAfter: Math.max(...actions.map(a => a.duration || 60))
}
}
if (requiresVerification) {
return {
status: 200,
requiresVerification: true,
challenge: actions.find(a => a.actionType === 'challenge')?.metadata.challenge,
message: 'Additional verification required'
}
}
return {
status: 200,
mitigated: actions.length > 0,
actions: actions.map(a => ({ type: a.actionType, strategy: a.strategyId }))
}
}
private recordMitigationActions(actions: MitigationAction[], ip: string): void {
if (!this.activeMitigations.has(ip)) {
this.activeMitigations.set(ip, [])
}
this.activeMitigations.get(ip)!.push(...actions)
// Clean up old actions (older than 1 hour)
const oneHourAgo = Date.now() - 60 * 60 * 1000
for (const [ipKey, ipActions] of this.activeMitigations.entries()) {
const recentActions = ipActions.filter(a => a.timestamp > oneHourAgo)
if (recentActions.length === 0) {
this.activeMitigations.delete(ipKey)
} else {
this.activeMitigations.set(ipKey, recentActions)
}
}
}
private initializeDefaultStrategies(): void {
const defaultStrategies: MitigationStrategy[] = [
{
id: 'critical_block',
name: 'Critical Risk Blocking',
type: 'block',
conditions: {
riskLevel: ['critical'],
botTypes: [],
trafficPatterns: [],
requestVolume: 0
},
actions: {
block: {
duration: 60,
reason: 'Critical risk bot activity detected'
}
},
priority: 10,
enabled: true
},
{
id: 'high_risk_challenge',
name: 'High Risk Challenge',
type: 'challenge',
conditions: {
riskLevel: ['high'],
botTypes: [],
trafficPatterns: [],
requestVolume: 0
},
actions: {
challenge: {
type: 'captcha',
difficulty: 'medium',
timeout: 300
}
},
priority: 9,
enabled: true
},
{
id: 'burst_rate_limit',
name: 'Burst Traffic Rate Limiting',
type: 'rate_limiting',
conditions: {
riskLevel: [],
botTypes: [],
trafficPatterns: ['burst'],
requestVolume: 50
},
actions: {
rateLimit: {
requestsPerMinute: 10,
burstLimit: 5,
windowSize: 60
}
},
priority: 8,
enabled: true
},
{
id: 'scraper_delay',
name: 'Scraper Delay',
type: 'delay',
conditions: {
riskLevel: [],
botTypes: ['scraper'],
trafficPatterns: [],
requestVolume: 0
},
actions: {
delay: {
milliseconds: 2000,
jitter: true
}
},
priority: 7,
enabled: true
},
{
id: 'credential_stuffer_block',
name: 'Credential Stuffing Block',
type: 'block',
conditions: {
riskLevel: ['medium', 'high', 'critical'],
botTypes: ['credential_stuffer'],
trafficPatterns: [],
requestVolume: 0
},
actions: {
block: {
duration: 30,
reason: 'Credential stuffing attempt detected'
}
},
priority: 10,
enabled: true
}
]
defaultStrategies.forEach(strategy => {
this.strategies.set(strategy.id, strategy)
})
}
private startMitigationMonitoring(): void {
// Monitor mitigation effectiveness
setInterval(() => {
this.analyzeMitigationEffectiveness()
}, 10 * 60 * 1000) // Every 10 minutes
}
private analyzeMitigationEffectiveness(): void {
for (const [ip, actions] of this.activeMitigations.entries()) {
const recentActions = actions.filter(a => a.timestamp > Date.now() - 30 * 60 * 1000)
if (recentActions.length > 5) {
console.log(`High mitigation frequency for IP ${ip}: ${recentActions.length} actions in 30 minutes`)
// Consider escalating mitigation
this.escalateMitigationForIP(ip)
}
}
}
private escalateMitigationForIP(ip: string): void {
// In production, escalate to more aggressive mitigation
console.log(`Escalating mitigation for persistent offender IP: ${ip}`)
}
// Strategy management
addStrategy(strategy: MitigationStrategy): void {
this.strategies.set(strategy.id, strategy)
}
removeStrategy(strategyId: string): void {
this.strategies.delete(strategyId)
}
updateStrategy(strategyId: string, updates: Partial<MitigationStrategy>): void {
const existing = this.strategies.get(strategyId)
if (existing) {
this.strategies.set(strategyId, { ...existing, ...updates })
}
}
getActiveMitigations(ip?: string): MitigationAction[] {
if (ip) {
return this.activeMitigations.get(ip) || []
}
return Array.from(this.activeMitigations.values()).flat()
}
getMitigationStats(): Record<string, { triggered: number; effective: number; falsePositive: number }> {
return Object.fromEntries(this.mitigationStats.entries())
}
}
// Integration with Express.js
class MitigationMiddleware {
private mitigationEngine: AdvancedMitigationEngine
constructor() {
this.mitigationEngine = new AdvancedMitigationEngine()
}
async applyMitigation(req: any, res: any, next: any): Promise<void> {
// Skip mitigation for health checks and internal requests
if (req.path.startsWith('/health') || req.path.startsWith('/internal')) {
return next()
}
// Get bot detection results
const botDetection = req.botDetection
if (!botDetection) {
return next()
}
try {
// Apply mitigation based on risk assessment
const mitigation = await this.mitigationEngine.applyMitigation(
botDetection.prediction,
{
ip: req.ip,
sessionId: req.sessionID,
userAgent: req.get('User-Agent'),
timestamp: Date.now()
},
[] // Would be populated with actual patterns
)
// Add mitigation info to request
req.mitigation = mitigation
// Handle blocking
if (mitigation.blocked) {
return res.status(mitigation.response.status).json(mitigation.response)
}
// Handle challenges
if (mitigation.requiresVerification) {
// In production, serve challenge page or API
return res.status(mitigation.response.status).json(mitigation.response)
}
next()
} catch (error) {
console.error('Mitigation middleware error:', error)
next()
}
}
}
// Initialize mitigation system
const mitigationEngine = new AdvancedMitigationEngine()
const mitigationMiddleware = new MitigationMiddleware()
// Apply mitigation middleware
app.use('/api', mitigationMiddleware.applyMitigation)
// Mitigation management endpoints
app.get('/api/mitigation/strategies', (req, res) => {
const strategies = Array.from(mitigationEngine['strategies'].values())
res.json({
strategies,
timestamp: new Date().toISOString()
})
})
app.post('/api/mitigation/strategies', (req, res) => {
try {
const strategy: MitigationStrategy = req.body
if (!strategy.id || !strategy.name || !strategy.type) {
return res.status(400).json({ error: 'Invalid strategy format' })
}
mitigationEngine.addStrategy(strategy)
res.json({
message: 'Strategy added successfully',
strategy,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Strategy creation error:', error)
res.status(500).json({ error: 'Strategy creation failed' })
}
})
app.put('/api/mitigation/strategies/:id', (req, res) => {
try {
const { id } = req.params
const updates = req.body
mitigationEngine.updateStrategy(id, updates)
res.json({
message: 'Strategy updated successfully',
strategyId: id,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Strategy update error:', error)
res.status(500).json({ error: 'Strategy update failed' })
}
})
app.delete('/api/mitigation/strategies/:id', (req, res) => {
try {
const { id } = req.params
mitigationEngine.removeStrategy(id)
res.json({
message: 'Strategy removed successfully',
strategyId: id,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Strategy removal error:', error)
res.status(500).json({ error: 'Strategy removal failed' })
}
})
app.get('/api/mitigation/active', (req, res) => {
const { ip } = req.query
const mitigations = mitigationEngine.getActiveMitigations(ip as string)
res.json({
mitigations,
count: mitigations.length,
timestamp: new Date().toISOString()
})
})
app.get('/api/mitigation/stats', (req, res) => {
const stats = mitigationEngine.getMitigationStats()
res.json({
stats,
timestamp: new Date().toISOString()
})
})
console.log('Advanced mitigation strategies system initialized')Bot Detection Dashboard and Monitoring
// Real-time dashboard for bot detection monitoring and analytics
interface DashboardMetrics {
timestamp: number
totalRequests: number
botRequests: number
humanRequests: number
blockedRequests: number
challengedRequests: number
averageResponseTime: number
systemHealth: 'healthy' | 'warning' | 'critical'
topBotTypes: Array<{ type: string; count: number; percentage: number }>
topRiskyIPs: Array<{ ip: string; riskScore: number; requests: number }>
patternTrends: Array<{ pattern: string; trend: 'increasing' | 'decreasing' | 'stable'; change: number }>
}
interface AlertRule {
id: string
name: string
condition: {
metric: string
operator: '>' | '<' | '=' | '>=' | '<='
threshold: number
duration: number // minutes
}
severity: 'low' | 'medium' | 'high' | 'critical'
enabled: boolean
lastTriggered?: number
notificationChannels: string[]
}
interface AlertNotification {
ruleId: string
timestamp: number
severity: string
title: string
description: string
metrics: Record<string, number>
recommendations: string[]
}
class BotDetectionDashboard {
private metrics: DashboardMetrics[] = []
private alertRules: Map<string, AlertRule> = new Map()
private alertHistory: AlertNotification[] = []
private subscribers: Set<any> = new Set()
constructor() {
this.initializeDefaultAlertRules()
this.startMetricsCollection()
this.startAlertMonitoring()
}
async getCurrentMetrics(): Promise<DashboardMetrics> {
// Aggregate current metrics from various sources
const currentMetrics = await this.collectCurrentMetrics()
// Calculate trends and health status
const healthStatus = this.calculateSystemHealth(currentMetrics)
const trends = await this.calculatePatternTrends()
return {
...currentMetrics,
systemHealth: healthStatus,
patternTrends: trends,
topBotTypes: await this.getTopBotTypes(),
topRiskyIPs: await this.getTopRiskyIPs()
}
}
private async collectCurrentMetrics(): Promise<Partial<DashboardMetrics>> {
// In production, collect from monitoring systems
// For demo, simulate metrics collection
return {
timestamp: Date.now(),
totalRequests: Math.floor(Math.random() * 10000) + 5000,
botRequests: Math.floor(Math.random() * 1000) + 200,
humanRequests: Math.floor(Math.random() * 8000) + 4000,
blockedRequests: Math.floor(Math.random() * 100) + 20,
challengedRequests: Math.floor(Math.random() * 200) + 50,
averageResponseTime: Math.random() * 200 + 100
}
}
private calculateSystemHealth(metrics: Partial<DashboardMetrics>): 'healthy' | 'warning' | 'critical' {
const botPercentage = metrics.botRequests! / metrics.totalRequests!
const errorRate = metrics.blockedRequests! / metrics.totalRequests!
if (botPercentage > 0.3 || errorRate > 0.1) {
return 'critical'
}
if (botPercentage > 0.15 || errorRate > 0.05) {
return 'warning'
}
return 'healthy'
}
private async calculatePatternTrends(): Promise<Array<{ pattern: string; trend: 'increasing' | 'decreasing' | 'stable'; change: number }>> {
// In production, analyze historical pattern data
// For demo, simulate trend analysis
const patterns = ['burst', 'periodic', 'sequential', 'suspicious']
return patterns.map(pattern => ({
pattern,
trend: ['increasing', 'decreasing', 'stable'][Math.floor(Math.random() * 3)] as any,
change: (Math.random() - 0.5) * 20
}))
}
private async getTopBotTypes(): Promise<Array<{ type: string; count: number; percentage: number }>> {
const botTypes = [
{ type: 'scraper', count: Math.floor(Math.random() * 200) + 50 },
{ type: 'credential_stuffer', count: Math.floor(Math.random() * 100) + 20 },
{ type: 'spammer', count: Math.floor(Math.random() * 150) + 30 },
{ type: 'click_fraud', count: Math.floor(Math.random() * 80) + 10 }
]
const total = botTypes.reduce((sum, type) => sum + type.count, 0)
return botTypes
.sort((a, b) => b.count - a.count)
.slice(0, 5)
.map(type => ({
...type,
percentage: Math.round((type.count / total) * 100)
}))
}
private async getTopRiskyIPs(): Promise<Array<{ ip: string; riskScore: number; requests: number }>> {
// In production, get from risk scoring system
// For demo, simulate risky IP data
const riskyIPs = []
for (let i = 0; i < 10; i++) {
riskyIPs.push({
ip: `192.168.1.${Math.floor(Math.random() * 255)}`,
riskScore: Math.floor(Math.random() * 100),
requests: Math.floor(Math.random() * 1000) + 100
})
}
return riskyIPs
.sort((a, b) => b.riskScore - a.riskScore)
.slice(0, 10)
}
private initializeDefaultAlertRules(): void {
const defaultRules: AlertRule[] = [
{
id: 'high_bot_percentage',
name: 'High Bot Traffic Percentage',
condition: {
metric: 'botPercentage',
operator: '>',
threshold: 25,
duration: 5
},
severity: 'high',
enabled: true,
notificationChannels: ['email', 'slack']
},
{
id: 'critical_system_health',
name: 'Critical System Health',
condition: {
metric: 'systemHealth',
operator: '=',
threshold: 0, // critical
duration: 2
},
severity: 'critical',
enabled: true,
notificationChannels: ['email', 'slack', 'sms']
},
{
id: 'rapid_response_time_increase',
name: 'Rapid Response Time Increase',
condition: {
metric: 'averageResponseTime',
operator: '>',
threshold: 500,
duration: 3
},
severity: 'medium',
enabled: true,
notificationChannels: ['email']
}
]
defaultRules.forEach(rule => {
this.alertRules.set(rule.id, rule)
})
}
private startMetricsCollection(): void {
// Collect metrics every minute
setInterval(async () => {
const metrics = await this.getCurrentMetrics()
this.metrics.push(metrics)
// Keep only last 24 hours of metrics
const twentyFourHoursAgo = Date.now() - 24 * 60 * 60 * 1000
this.metrics = this.metrics.filter(m => m.timestamp > twentyFourHoursAgo)
// Notify subscribers
this.notifySubscribers(metrics)
}, 60 * 1000)
}
private startAlertMonitoring(): void {
// Check alerts every minute
setInterval(() => {
this.checkAlertConditions()
}, 60 * 1000)
}
private async checkAlertConditions(): Promise<void> {
const currentMetrics = await this.getCurrentMetrics()
for (const rule of this.alertRules.values()) {
if (!rule.enabled) continue
const shouldTrigger = await this.evaluateAlertCondition(rule, currentMetrics)
if (shouldTrigger) {
await this.triggerAlert(rule, currentMetrics)
}
}
}
private async evaluateAlertCondition(rule: AlertRule, metrics: DashboardMetrics): Promise<boolean> {
// Check if condition has been met for the required duration
const recentMetrics = this.metrics.slice(-rule.condition.duration)
if (recentMetrics.length < rule.condition.duration) {
return false
}
// Check if all recent metrics meet the condition
return recentMetrics.every(m => {
const value = this.getMetricValue(m, rule.condition.metric)
return this.compareValues(value, rule.condition.threshold, rule.condition.operator)
})
}
private getMetricValue(metrics: DashboardMetrics, metricName: string): number {
switch (metricName) {
case 'botPercentage':
return (metrics.botRequests / metrics.totalRequests) * 100
case 'systemHealth':
return metrics.systemHealth === 'critical' ? 0 : metrics.systemHealth === 'warning' ? 1 : 2
case 'averageResponseTime':
return metrics.averageResponseTime
default:
return 0
}
}
private compareValues(value: number, threshold: number, operator: string): boolean {
switch (operator) {
case '>': return value > threshold
case '<': return value < threshold
case '=': return Math.abs(value - threshold) < 0.01
case '>=': return value >= threshold
case '<=': return value <= threshold
default: return false
}
}
private async triggerAlert(rule: AlertRule, metrics: DashboardMetrics): Promise<void> {
// Check if already triggered recently (avoid spam)
const recentAlerts = this.alertHistory.filter(a =>
a.ruleId === rule.id && a.timestamp > Date.now() - 60 * 60 * 1000 // 1 hour cooldown
)
if (recentAlerts.length > 0) {
return
}
const alert: AlertNotification = {
ruleId: rule.id,
timestamp: Date.now(),
severity: rule.severity,
title: rule.name,
description: `Alert condition met: ${rule.condition.metric} ${rule.condition.operator} ${rule.condition.threshold}`,
metrics: {
[rule.condition.metric]: this.getMetricValue(metrics, rule.condition.metric)
},
recommendations: this.generateAlertRecommendations(rule, metrics)
}
this.alertHistory.push(alert)
rule.lastTriggered = Date.now()
// Send notifications
await this.sendAlertNotifications(alert, rule)
console.log(`🚨 Alert triggered: ${alert.title}`)
}
private generateAlertRecommendations(rule: AlertRule, metrics: DashboardMetrics): string[] {
switch (rule.id) {
case 'high_bot_percentage':
return [
'Review bot detection thresholds',
'Increase mitigation aggressiveness',
'Check for new bot patterns'
]
case 'critical_system_health':
return [
'Immediate investigation required',
'Check system resources',
'Review recent configuration changes',
'Consider emergency mitigation measures'
]
case 'rapid_response_time_increase':
return [
'Check system performance',
'Review recent traffic spikes',
'Consider scaling resources'
]
default:
return ['Review system status and recent changes']
}
}
private async sendAlertNotifications(alert: AlertNotification, rule: AlertRule): Promise<void> {
// In production, send to configured channels (email, Slack, etc.)
console.log(`Sending alert notification to channels: ${rule.notificationChannels.join(', ')}`)
}
// WebSocket support for real-time dashboard updates
subscribeToUpdates(ws: any): void {
this.subscribers.add(ws)
ws.on('close', () => {
this.subscribers.delete(ws)
})
}
private notifySubscribers(metrics: DashboardMetrics): void {
const update = {
type: 'dashboard_update',
metrics,
timestamp: new Date().toISOString()
}
this.subscribers.forEach(ws => {
if (ws.readyState === 1) { // WebSocket.OPEN
ws.send(JSON.stringify(update))
}
})
}
// Alert management
addAlertRule(rule: AlertRule): void {
this.alertRules.set(rule.id, rule)
}
removeAlertRule(ruleId: string): void {
this.alertRules.delete(ruleId)
}
getAlertHistory(hours: number = 24): AlertNotification[] {
const cutoff = Date.now() - hours * 60 * 60 * 1000
return this.alertHistory.filter(a => a.timestamp > cutoff)
}
getAlertRules(): AlertRule[] {
return Array.from(this.alertRules.values())
}
}
// Initialize dashboard
const botDetectionDashboard = new BotDetectionDashboard()
// Dashboard API endpoints
app.get('/api/dashboard/metrics', async (req, res) => {
try {
const metrics = await botDetectionDashboard.getCurrentMetrics()
res.json({
metrics,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Dashboard metrics error:', error)
res.status(500).json({ error: 'Metrics unavailable' })
}
})
app.get('/api/dashboard/alerts', (req, res) => {
const { hours = 24 } = req.query
const alerts = botDetectionDashboard.getAlertHistory(Number(hours))
res.json({
alerts,
count: alerts.length,
timestamp: new Date().toISOString()
})
})
app.get('/api/dashboard/alert-rules', (req, res) => {
const rules = botDetectionDashboard.getAlertRules()
res.json({
rules,
timestamp: new Date().toISOString()
})
})
app.post('/api/dashboard/alert-rules', (req, res) => {
try {
const rule: AlertRule = req.body
if (!rule.id || !rule.name || !rule.condition) {
return res.status(400).json({ error: 'Invalid alert rule format' })
}
botDetectionDashboard.addAlertRule(rule)
res.json({
message: 'Alert rule added successfully',
rule,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Alert rule creation error:', error)
res.status(500).json({ error: 'Alert rule creation failed' })
}
})
app.delete('/api/dashboard/alert-rules/:id', (req, res) => {
try {
const { id } = req.params
botDetectionDashboard.removeAlertRule(id)
res.json({
message: 'Alert rule removed successfully',
ruleId: id,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Alert rule removal error:', error)
res.status(500).json({ error: 'Alert rule removal failed' })
}
})
// Real-time dashboard WebSocket
app.ws('/api/dashboard/live', (ws: any) => {
console.log('Dashboard client connected')
botDetectionDashboard.subscribeToUpdates(ws)
// Send initial metrics
botDetectionDashboard.getCurrentMetrics().then(metrics => {
ws.send(JSON.stringify({
type: 'initial_metrics',
metrics,
timestamp: new Date().toISOString()
}))
}).catch(error => {
console.error('Initial metrics error:', error)
})
})
console.log('Bot detection dashboard and monitoring system initialized')Protection Mechanisms
Successful implementation requires understanding the technical landscape and choosing appropriate strategies.
Implementation Approaches
Modern Solutions
- Cloud-native architectures
- Microservices integration
- Real-time processing capabilities
- Automated scaling mechanisms
Bot Detection and Mitigation Architecture
Implementation Strategies {#implementation-strategies}
Deploy comprehensive bot detection system.
class ComprehensiveBotProtection {
private detector: BotDetectionEngine
private mitigator: BotMitigationSystem
constructor() {
this.detector = new BotDetectionEngine()
this.mitigator = new BotMitigationSystem()
}
async protect(req: any): Promise<{
allowed: boolean
action: string
reason?: string
}> {
const analysis = await this.detector.analyzeRequest(req)
if (analysis.isBot && analysis.confidence > 80) {
return await this.mitigator.mitigate(analysis)
}
return { allowed: true, action: 'allow' }
}
}Monitoring and Detection {#monitoring-and-detection}
Track bot activity and detection accuracy.
Key Metrics:
- Bot detection rate
- False positive rate
- Challenge solve rate (for CAPTCHAs)
- Blocked request percentage
- Bot pattern evolution
Incident Response Planning {#incident-response-planning}
Respond to bot attack scenarios.
const BOT_RESPONSE_PLAYBOOKS = {
'scraping_bot': {
action: 'throttle',
severity: 'medium',
escalate: false
},
'ddos_bot': {
action: 'block',
severity: 'critical',
escalate: true
},
'credential_stuffing': {
action: 'challenge',
severity: 'high',
escalate: true
}
}Compliance and Best Practices {#compliance-and-best-practices}
Balance bot protection with legitimate users.
Best Practices:
- Minimize false positives (don't block real users)
- Provide clear feedback for challenged requests
- Maintain audit logs for bot detection
- Regular testing with automation tools
- Adaptive thresholds based on traffic patterns
Conclusion {#conclusion}
Effective bot detection requires multi-layered analysis combining behavioral patterns, fingerprinting, challenge-response, and rate limiting. Success depends on balancing security with user experience, minimizing false positives, and continuously adapting to evolving bot techniques.
Key success factors include implementing multiple detection signals, using confidence-based decisions, providing fallback for legitimate automation, monitoring false positive rates, and maintaining up-to-date bot signature databases.
Protect your applications from bots with our detection APIs, designed to identify and mitigate automated traffic while maintaining excellent user experience for legitimate visitors.