Chia sẻ kiến thức lập trình

Anti-flooding cho Socket

Cơ chế chống DDoS

Tấn công từ chối dịch vụ (DDoS) là một nỗ lực của kẻ tấn công làm cho máy chủ của chúng ta không thể nhận thêm bất kì request nào nữa, dẫn đến việc nó không thể phục vụ bất kỳ user nào khác ngoài kẻ tấn công. Theo kinh nghiệm của mình thì có 2 kiểu tấn công chính:

  1. Kẻ tấn công khai thác lỗ hổng từ một API xử lý rất nặng nào đó, và gửi request liên tục vào đây làm cho server dùng toàn bộ tài nguyên chỉ để phục vụ cho kẻ tấn công này.
  2. Kẻ tấn công sử dụng nhiều nguồn tài nguyên gấp rất nhiều lần khả năng chịu đựng của server, khiến cho nó không còn khả năng chấp nhận những request khác nữa

Đối với trường hợp thứ 2, nếu kẻ tấn công có thể huy động được hàng nghìn thiết bị để giả lập giống như user thật điều đó sẽ rất khó để chống đỡ, vì trong 1 luồng dữ liệu socket, việc xử lý 1 request là tương đối nặng nề.

Giải pháp phần mềm

Không có một phương pháp nào hiệu quả một 100% đối với các cuộc tấn công DDoS, vậy nên cách tốt nhất là kết hợp cả sức mạnh của phần cứng lẫn phần mềm. trong phạm vi của bài viết này mình sẽ chỉ nói đến cách thức xử lý của phần mềm thôi nhé.

Để giải. bài toán hóc búa này thì ezyfox-server đã cung cấp sẵn giải pháp thông qua việc cấu hình file xml đơn giản kiểu thế này:

<session-management>
    <session-max-idle-time>15000</session-max-idle-time>
    <session-max-waiting-time>30000</session-max-waiting-time>
    <session-max-request-per-second>
        <value>20</value>
        <action>DISCONNECT_SESSION</action>
    </session-max-request-per-second>
</session-management>

Tuy nhiên mình muốn viết sâu hơn để các bạn thấy được ezyfox-server đã xử lý như thế nào để đạt được mục tiêu tối đa hoá khả năng chống DDoS nhé.

Hạn chế số lượng session

Mỗi client sẽ được đại diện bởi 1 session, nên việc đầu tiên chúng ta cần làm là giới hạn tối thiểu số lượng session tồn tại trong server, vì sao vậy? Vì mỗi một session lại chiếm một số lượng tài nguyên nhất định, nên cần giữ số lượng session ở mức thấp nhất có thể bằng cách sử dụng 1 thread để thanh tra và loại bỏ các loại session:

  • Đã quá thời gian đăng nhập cho phép
  • Đã quá lâu không có request nào gửi đến server

Một session nếu muốn giữ kết nối với server phải gửi command ping định kì, có thể 3 hoặc 5 giây tuỳ theo bạn cấu hình. Nếu quá 15 giây mà không có bất kì lệnh ping nào chẳng gửi đến server chẳng hạn, server sẽ chủ động ngắt kết nối với session này.

Hạn chế số lượng request

Sẽ có rất nhiều request được gửi đến server, tuy nhiên server cần giới hạn số lượng request tối đa cho một client, ví dụ như trong hình, server sẽ chỉ chấp nhận các request màu xanh tại 1 thời điểm, còn những request màu đỏ, do vượt quá số lượng mà server có thể chịu tải sẽ bị drop và không được xử lý.

Các request cũng sẽ không được xử lý ngay, mà còn phải trải qua 1 quá trình điều phối xoay vòng để có thểm đảm bảo tài nguyên được phân phối một cách hợp lý.

Phân phối đều tài nguyên

Để server không chỉ phục vụ duy nhất 1 client nào đó, chúng ta cần phải xử dụng cơ chế "băng đạn tròn" để xoay tua liên tục. Mỗi client sẽ có 1 request queue, mỗi lần xử lý sẽ lấy 1 request từ một queue và rồi lại cho queue này về cuối. Ví dụ:

Queue1: A1, A2, A3
Queue2: B1, B2, B3
Queue3: C1, C2, C3

Lượt xử lý đầu tiên sẽ lấy A3 ra để xử lý và trạng thái sẽ là:

Queue2: B1, B2, B3
Queue3: C1, C2, C3
Queue1: A1, A2

Lượt xử lý tiếp theo sẽ lấy B3 ra để xử lý và trạng thái sẽ là:

Queue3: C1, C2, C3
Queue1: A1, A2
Queue2: B1, B2

Cứ như vậy cho đến khi nào các queue rỗng hết thì thôi. Điều này sẽ đảm bảo server sẽ phục vụ đều cho tất cả user

Tổng kết

Lại một lần nữa chúng ta thấy được sự phức tạp bên trong của một socket server. Nhưng chính có sự phức tạp này mà chúng ta có thể yên tâm sử dụng mà không cần phải lăn tăn gì cả. Tuy nhiên cũng lại 1 lần nữa, đây cũng chỉ là một phần trong kiến trúc của các framework socket như ezyfox-server mà thôi. Cùng chờ đón những bài viết tiếp theo mọi người nhé.

Share: