2014-10-10 13 views
7

Tôi đang cố gắng nghĩ ra cách giúp giảm thiểu thiệt hại trên ứng dụng node.js của mình nếu tôi bị tấn công DDOS. Tôi muốn giới hạn các yêu cầu trên mỗi IP. Tôi muốn giới hạn mọi địa chỉ IP với quá nhiều yêu cầu mỗi giây. Ví dụ: Không có địa chỉ IP nào có thể vượt quá 10 yêu cầu mỗi 3 giây.Làm thế nào để hạn chế số lượng yêu cầu cho mỗi ip trong Node.JS?

Cho đến nay tôi đã đưa ra với điều này:

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

Bạn có thể bảo vệ chống lại hệ điều hành DOS nhưng không chống lại một cuộc tấn công DISTRIBUTEDdos với điều đó! – loveNoHate

+1

Tôi khuyên bạn nên đặt Nginx hoặc thứ gì đó ở phía trước máy chủ ứng dụng Node.js của bạn. Bạn có nhiều công cụ hoạt động hiệu quả hơn và sau đó có thể cho phép các máy chủ ứng dụng của bạn làm những gì họ làm tốt nhất ... chạy ứng dụng của bạn. – Brad

Trả lời

4

Nếu bạn muốn tự xây dựng ở cấp máy chủ ứng dụng, bạn sẽ phải tạo cấu trúc dữ liệu ghi lại từng quyền truy cập gần đây từ địa chỉ IP cụ thể để khi có yêu cầu mới, bạn có thể nhìn lại thông qua lịch sử và xem liệu nó có đang thực hiện quá nhiều yêu cầu hay không. Nếu có, hãy từ chối mọi dữ liệu khác. Và, để giữ cho dữ liệu này không được chồng chất trong máy chủ của bạn, bạn cũng cần một số loại mã dọn dẹp để loại bỏ dữ liệu truy cập cũ.

Dưới đây là một ý tưởng cho một cách để làm điều đó (mã chưa được kiểm tra để minh họa cho ý tưởng):

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(); 
    } 
}); 

Phương pháp này cũng có những hạn chế thông thường xung quanh theo dõi địa chỉ IP. Nếu nhiều người dùng đang chia sẻ địa chỉ IP phía sau NAT, điều này sẽ coi tất cả họ là một người dùng và họ có thể bị chặn do hoạt động kết hợp của họ, không phải do hoạt động của một người dùng.


Nhưng, như những người khác đã cho biết, khi yêu cầu này đến máy chủ của bạn, một số thiệt hại DOS đã được thực hiện (nó đã lấy chu kỳ từ máy chủ của bạn). Nó có thể giúp cắt bỏ yêu cầu trước khi thực hiện các hoạt động đắt tiền hơn như các hoạt động cơ sở dữ liệu, nhưng nó thậm chí còn tốt hơn để phát hiện và chặn điều này ở mức cao hơn (như Nginx hoặc tường lửa hoặc cân bằng tải).

2

Tôi không nghĩ rằng đó là một cái gì đó nên được thực hiện ở mức độ máy chủ http. Về cơ bản, nó không ngăn người dùng tiếp cận máy chủ của bạn, ngay cả khi họ không thấy bất cứ thứ gì trong 15 phút.

Theo ý kiến ​​của tôi, bạn nên xử lý điều đó trong hệ thống của mình, sử dụng tường lửa. Mặc dù nó là một cuộc thảo luận cho ServerFault hoặc SuperUser, hãy để tôi cung cấp cho bạn một vài gợi ý.

  1. Sử dụng iptables để thiết lập một bức tường lửa trên điểm vào của bạn (máy chủ của bạn hoặc bất cứ điều gì khác mà bạn có thể sử dụng lên dòng). iptables cho phép bạn thiết lập giới hạn các kết nối tối đa cho mỗi IP. Đường cong học tập khá dốc mặc dù bạn không có nền tảng trong Mạng. Đó là cách truyền thống.

    Dưới đây là một nguồn lực tốt hướng tới người mới bắt đầu: Iptables for beginners

    Và một cái gì đó tương tự như những gì bạn cần ở đây: Unix StackExchange

  2. Gần đây tôi tình cờ gặp một gói thật sự tốt đẹp gọi là Uncomplicated Firewall (ufw) nó xảy ra để có một tùy chọn để hạn chế tốc độ kết nối cho mỗi IP và được thiết lập trong vài phút. Đối với những thứ phức tạp hơn, bạn vẫn sẽ cần iptables mặc dù.

    Tóm lại, như Brad nói,

    phép các máy chủ ứng dụng của bạn làm những gì họ cố gắng hết sức ... chạy ứng dụng của bạn.

    Và để tường lửa làm những gì họ làm tốt nhất, khởi động các IP không mong muốn từ máy chủ của bạn.

1

Không tốt nếu bạn sử dụng Nodejs lọc kết nối hoặc áp dụng chính sách kết nối như vậy.

Sẽ tốt hơn nếu bạn sử dụng Nginx trước NodeJS

Client -> Nginx -> Nodejs hoặc ứng dụng.

Nó không phải là khó khăn và rẻ tiền vì Ngnix là nguồn mở tooo.

Chúc may mắn.

Các vấn đề liên quan