如何在Node.JS中限制每个ip请求的数量?

我试图想方法来帮助如果我得到一个DDOS攻击我的node.js应用程序的损害最小化。 我想限制每个IP的请求。 我想限制每一个IP地址每秒这么多的请求。 例如:每3秒没有IP地址可以超过10个请求。

到目前为止,我已经想出了这个:

http.createServer(req, res, function() { if(req.connection.remoteAddress ?????? ) { block ip for 15 mins } } 

如果您想在应用程序服务器级别自行构build,则必须构build一个数据结构,logging每个来自特定IP地址的最近访问,以便在发出新请求时可以回顾历史logging一直在做太多的要求。 如果是这样,否认任何进一步的数据。 而且,为了防止这些数据堆积在你的服务器上,你还需要一些清理代码来清除旧的访问数据。

这里有一个方法来做到这一点(未经testing的代码来说明这个想法):

 function AccessLogger(n, t, blockTime) { this.qty = n; this.time = t; this.blockTime = blockTime; this.requests = {}; // schedule cleanup on a regular interval (every 30 minutes) this.interval = setInterval(this.age.bind(this), 30 * 60 * 1000); } AccessLogger.prototype = { check: function(ip) { var info, accessTimes, now, limit, cnt; // add this access this.add(ip); // should always be an info here because we just added it info = this.requests[ip]; accessTimes = info.accessTimes; // calc time limits now = Date.now(); limit = now - this.time; // short circuit if already blocking this ip if (info.blockUntil >= now) { return false; } // short circuit an access that has not even had max qty accesses yet if (accessTimes.length < this.qty) { return true; } cnt = 0; for (var i = accessTimes.length - 1; i >= 0; i--) { if (accessTimes[i] > limit) { ++cnt; } else { // assumes cnts are in time order so no need to look any more break; } } if (cnt > this.qty) { // block from now until now + this.blockTime info.blockUntil = now + this.blockTime; return false; } else { return true; } }, add: function(ip) { var info = this.requests[ip]; if (!info) { info = {accessTimes: [], blockUntil: 0}; this.requests[ip] = info; } // push this access time into the access array for this IP info.accessTimes.push[Date.now()]; }, age: function() { // clean up any accesses that have not been here within this.time and are not currently blocked var ip, info, accessTimes, now = Date.now(), limit = now - this.time, index; for (ip in this.requests) { if (this.requests.hasOwnProperty(ip)) { info = this.requests[ip]; accessTimes = info.accessTimes; // if not currently blocking this one if (info.blockUntil < now) { // if newest access is older than time limit, then nuke the whole item if (!accessTimes.length || accessTimes[accessTimes.length - 1] < limit) { delete this.requests[ip]; } else { // in case an ip is regularly visiting so its recent access is never old // we must age out older access times to keep them from // accumulating forever if (accessTimes.length > (this.qty * 2) && accessTimes[0] < limit) { index = 0; for (var i = 1; i < accessTimes.length; i++) { if (accessTimes[i] < limit) { index = i; } else { break; } } // remove index + 1 old access times from the front of the array accessTimes.splice(0, index + 1); } } } } } } }; var accesses = new AccessLogger(10, 3000, 15000); // put this as one of the first middleware so it acts // before other middleware spends time processing the request app.use(function(req, res, next) { if (!accesses.check(req.connection.remoteAddress)) { // cancel the request here res.end("No data for you!"); } else { next(); } }); 

这种方法在IP地址监视方面也有通常的限制。 如果多个用户在NAT后面共享一个IP地址,这将把他们全部视为一个用户,并且可能由于他们的联合活动而被阻止,而不是因为单个用户的活动。


但是,正如其他人所说,当请求到达你的服务器的时候,一些DOS损坏已经完成(它已经从你的服务器周期)。 在执行诸如数据库操作之类的更昂贵的操作之前,可能会帮助切断请求,但是在更高级别(例如Nginx或防火墙或负载平衡器)检测并阻止此操作会更好。

我不认为这应该在http服务器级别上完成。 基本上,它不会阻止用户访问您的服务器,即使他们在15分钟内不会看到任何内容。

在我看来,你应该在你的系统中使用防火墙来处理这个问题。 虽然这更多的是关于ServerFault或者SuperUser的讨论,但是让我给你一些指点。

  1. 使用iptables在您的入口点(您的服务器或任何您有权访问的线路)上设置防火墙 。 iptables允许您设置每个IP的最大连接数限制。 如果你在networking中没有背景,学习曲线会非常陡峭。 这是传统的方式。

    这是一个适合初学者的好资源:初学者的iptables

    和你需要的东西类似: Unix StackExchange

  2. 我最近遇到一个名为Uncomplicated Firewall(ufw)的非常好的软件包,它恰好有一个选项来限制每个IP的连接速度,并在几分钟内设置。 对于更复杂的东西,你仍然需要iptables。

    最后,就像布拉德所说,

    让你的应用程序服务器做他们最擅长的事情…运行你的应用程序

    让防火墙做他们最擅长的事情,从服务器中剔除不需要的IP。

如果您使用Nodejs过滤连接或应用连接策略,那就不好了。

在NodeJS前面使用Nginx会更好

客户端 – > Nginx – > Nodejs或应用程序。

这并不难,也很便宜,因为Ngnix也是开源的。

祝你好运。