UNPKG

@aikidosec/firewall

Version:

Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.

72 lines (71 loc) 3.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AttackWaveDetector = void 0; const LRUMap_1 = require("../../ratelimiting/LRUMap"); const isWebScanner_1 = require("./isWebScanner"); class AttackWaveDetector { constructor(options = {}) { var _a, _b, _c, _d, _e; this.attackWaveThreshold = (_a = options.attackWaveThreshold) !== null && _a !== void 0 ? _a : 15; // Default: 15 requests this.attackWaveTimeFrame = (_b = options.attackWaveTimeFrame) !== null && _b !== void 0 ? _b : 60 * 1000; // Default: 1 minute this.minTimeBetweenEvents = (_c = options.minTimeBetweenEvents) !== null && _c !== void 0 ? _c : 20 * 60 * 1000; // Default: 20 minutes this.maxLRUEntries = (_d = options.maxLRUEntries) !== null && _d !== void 0 ? _d : 10000; // Default: 10,000 entries this.maxSamplesPerIP = (_e = options.maxSamplesPerIP) !== null && _e !== void 0 ? _e : 15; // Default: 15 samples this.suspiciousRequests = new LRUMap_1.LRUMap(this.maxLRUEntries, this.attackWaveTimeFrame); this.sentEventsMap = new LRUMap_1.LRUMap(this.maxLRUEntries, this.minTimeBetweenEvents); } /** * Checks if the request is part of an attack wave * Will report to core once in a defined time frame when the threshold is exceeded * @returns true if an attack wave is detected and should be reported */ check(context) { if (!context.remoteAddress) { return false; } const ip = context.remoteAddress; const sentEventTime = this.sentEventsMap.get(ip); if (sentEventTime) { // The last event was sent recently return false; } // In isWebScanner we use `context.route`, `context.route` is always created from `context.url` if (!context.method || !context.url) { return false; } if (!(0, isWebScanner_1.isWebScanner)(context)) { return false; } const suspiciousRequests = this.suspiciousRequests.get(ip) || { count: 0, samples: [], }; suspiciousRequests.count += 1; suspiciousRequests.samples = this.trackSample({ method: context.method, url: context.url, }, suspiciousRequests.samples); this.suspiciousRequests.set(ip, suspiciousRequests); if (suspiciousRequests.count < this.attackWaveThreshold) { return false; } this.sentEventsMap.set(ip, performance.now()); return true; } getSamplesForIP(ip) { var _a; return ((_a = this.suspiciousRequests.get(ip)) === null || _a === void 0 ? void 0 : _a.samples) || []; } trackSample(request, samples) { if (samples.length >= this.maxSamplesPerIP) { return [...samples]; } // Only store unique samples // We can't use a Set because we have objects if (samples.some((sample) => sample.method === request.method && sample.url === request.url)) { return [...samples]; } return [...samples, request]; } } exports.AttackWaveDetector = AttackWaveDetector;