앱 서버 수준에서이 자신을 구축하려는 경우, 새 요청이 도착하면, 다시 검색 할 수 있도록 특정 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 또는 방화벽 또는로드 밸런서)에서이를 탐지하고 차단하는 것이 좋습니다.
DOS를 방어 할 수는 있지만 DISTRIBUTEDdos 공격에 대해서는 방어 할 수 없습니다! – loveNoHate
Nginx 등을 Node.js 애플리케이션 서버 앞에 놓는 것이 좋습니다. 보다 효율적으로 작동하는 더 많은 도구가 있고 응용 프로그램 서버가 최선을 다해 응용 프로그램을 실행하게 할 수 있습니다. – Brad