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

标签 node.js

如果我受到 DDOS 攻击,我正在尝试想出一种方法来帮助最大限度地减少对我的 node.js 应用程序的损害。我想限制每个 IP 的请求。我想将每个 IP 地址限制为每秒请求数。例如:任何 IP 地址每 3 秒不能超过 10 个请求。

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

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

最佳答案

如果你想在应用程序服务器级别自己构建它,你将必须构建一个数据结构来记录来自特定 IP 地址的每个最近访问,以便当新请求到达时,你可以回顾历史并看看它是否一直在做太多的请求。如果是这样,请拒绝提供任何进一步的数据。而且,为了防止这些数据堆积在您的服务器中,您还需要某种清理代码来清除旧的访问数据。

这里有一个实现方法的想法(未经测试的代码来说明这个想法):

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 或防火墙或负载平衡器)检测并阻止它。

关于node.js - 如何在 Node.JS 中限制每个 ip 的请求量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26306863/

相关文章:

node.js STDIN 与 process.stdin 一起使用,而不是在管道传输时使用

javascript - 用于有条件编辑内容的 Form 或 div

javascript - 为什么我使用异步钩子(Hook) API 会导致 Node.js 异常终止?

javascript - 如何默认隐藏 Node.js 中的内部堆栈跟踪错误?

javascript - AngularJS 应用程序上的动态页面标题

javascript - 如何在 MERN 堆栈中定义架构和快速路由器?

node.js - 使用 GridFSBucket openDownloadStream 时出现文件未找到错误

node.js - 禁用未找到 eslint 规则的警告消息

mysql - Connect-Mongo 和 Express-session 无法正常工作

node.js - Node js中基于关系的ACL