Hạn chế DDoS Attack tầng giao vận TCP SYN Flood bằng Bloom filter.

Bài viết này mình viết về một phần kiến thức được học ở môn Toán Chuyên Đề do Sư phụ giảng dạy trên lớp đại học của mình hồi kỳ 2 năm 3. Trong một dịp ngồi mở lại kiến thức cũ để chuẩn bị hỗ trợ cho Sư huynh ở bên Cục An toàn thông tin, mình ngồi vọc vạch lại vài ý cốt lõi. Rất tiếc vì hồi đó mình gà mờ không biết lưu trữ project lên GitHub, do đó cái project C++ về Bloom Filter này cũng bay mất theo việc mình ngu 1 lần lỡ tay format ổ cứng khi cài lại win.

Nội dung bài viết này nói về phương thức hạn chế DDoS Attack bằng Bloom filter, do đó những khái niệm khác mình sẽ chỉ giới thiệu sơ bộ; có thể mình sẽ trình bày cụ thể từng khái niệm ở những bài viết khác.

DDoS Attack.

  • DoS (Denial of Service) Attack: Tấn công từ chối dịch vụ. Đây là solo, 1 vs 1! Người yêu bạn biết thể loại truyện bạn yêu thích là Kiếm Hiệp/Tiên Hiệp, nên đã rất tâm lý mua tặng bạn 1 bộ Lộc Đỉnh Ký - Kim Dung. Bạn thích quá đọc say sưa đâm ra không thèm đoái hoài gì đến việc anh zai Tech Lead yêu cầu config lại cái API Gateway. OK, chúc mừng bạn đã bị người yêu DoS thành công. Bạn đang trong trạng thái "từ chối dịch vụ", không tiếp nhận yêu cầu từ anh zai Tech Lead nữa → bị đuổi việc! 👏👏
  • DDoS (Distributed Denial of Service) Attack: Tấn công từ chối dịch vụ phân tán. Đây là hội đồng, rampage! Bạn đọc truyện rất chừng mực và vẫn nhớ đến yêu cầu của anh zai Tech Lead; nhưng 1 đám em gái mưa của bạn, đứa thì nhờ cài win, đứa thì nhờ sang chuyển nhà cùng, đứa thì nhờ giải bài tập hộ, ... Nói tóm lại, cuối cùng thì bạn cũng bị đám em gái mưa cùng người yêu của bạn DDoS thành công mà thôi! Nên kết cục của bạn vẫn là bị đuổi việc! 👻
  • Tóm lại, DoS là việc hacker tận dụng điểm yếu của máy chủ để làm cho máy chủ đó trở lên quá tải, dẫn đến không phục vụ nữa. Còn nếu máy chủ khỏe quá mà hacker không đánh sập được, thì nó sẽ chơi kiểu cục xúc DDoS. Hacker sẽ như 1 tướng quân, điều khiển một hệ thống quân đội bao gồm hàng ngàn máy tính khác (zombie) qua các handler như IRC (Internet Relay Chat) hay C&C (Command and Control), tất cả cùng lao lên một lúc. Server cho dù có kiên cố đến mấy, nếu không biết cách phát hiện thì cũng sập mà thôi.
  • Hacker kiếm đâu ra cả ngàn zombie? Đương nhiên đã muốn DDoS, thì trước tiên hacker sẽ phải phát tán một loại mã độc vào mạng Internet. Mấy thanh niên hacker rất giỏi trong việc crack cũng như dịch ngược phần mềm; chỉ cần người dùng download về sử dụng thì đúng là "mua 1 tặng 1", vô tình trở thành zombie cho hacker điều khiển từ xa. 
  • Điều khiển từ xa ở đây cũng chẳng phải cái gì cao siêu quá, đơn giản chỉ là gửi 1 HTTP Request tới 1 IP nào đó mà thôi. Trong thời đại IoT bùng nổ hiện nay, ngay cả cái nồi cơm điện cũng là 1 thành phần trong mạng IoT, do đó nó cũng có thể bị nhiễm mã độc và trở thành zombie và nghe theo sự điều khiển từ xa của hacker.

Tầng giao vận.

  • Trong mô hình OSI phân tầng cấu trúc hạ tầng mạng theo chiều dọc, tầng Giao vận (Transport layer) là tầng thứ 4 từ dưới lên, là nơi xử lý các gói tin TCP và UDP. Đây là nơi kiểm soát độ tin cậy của một kết nối, cũng như xử lý tắc nghẽn. 
  • Nếu bạn đã từng lập trình socket, chắc chắn sẽ biết đến các khái niệm IP, PORT. Về cơ bản, đây cũng chính là những tham số quan trọng có mặt trong một gói tin TCP hoặc UDP.

TCP SYN Flood.

  • Trong tầng giao vận, có một khái niệm là "bắt tay 3 bước" (three way handshake). Quá trình xác nhận bắt tay 3 bước thành công này được mở màn bằng gói tin SYN và kết thúc bằng gói tin ACK(Seq+1). Khi DoS/DDoS attack xảy ra, zombie sẽ chỉ gửi toàn SYN mà không gửi ACK(Seq+1) để xác nhận bắt tay 3 bước, vì đơn giản hacker đâu có sở hữu zombie, chỉ là điều khiển zombie mà thôi. Server nhận được SYN nào thì cũng phải tạo thread/buffer để phục vụ cho SYN đó. Dẫn đến cả những SYN fake từ zombie gửi đến cũng được tạo thread/buffer để chờ phục vụ. Điều này sẽ dẫn đến việc quá tải server.
  • Giả sử nhà bạn có rất nhiều ghế ngồi để tiếp khách. Bạn rất lịch sự, nếu có ai tới thăm, bạn cũng sẽ để dành 1 ghế trống đợi họ đến. Tự nhiên đâu cả 1 đám người đi ngoài đường nói bâng quơ là sẽ vào thăm nhà bạn, nhưng thực chất họ chỉ lởn vởn ngoài cổng. Vậy là bạn phải để dành 1 đống ghế trống đó cho đám người đó mà không dùng vào việc gì cả. Càng lâu bạn sẽ càng không tiếp được những vị khác thực lòng, và bạn sớm muộn cũng sẽ hết ghế không thể tiếp ai được nữa. 👻 
  • Ngoài ra, còn 1 cách thức nữa để thực hiện TCP SYN Flood. Hacker thường gửi những yêu cầu giả mạo server thay vì yêu cầu trực tiếp tới các máy tính phản xạ; do đó các máy tính này trả lời lại các yêu cầu đó tới server, gây ngập lụt băng thông.
  • Đây là chuyện thật. Con bạn mình có 1 lần bị ai đó post số điện thoại lên group gái gọi Hà Nội. Thế là nó bị các anh zai checker ở Hà Nội DDoS cho mấy ngày liền. Khổ thân con bé. 😂

Bloom Filter (BF)

  • Bloom filter (bộ lọc Bloom) là 1 cấu trúc dữ liệu xác suất. Nó có tác dụng kiểm tra xem một giá trị nào đó có nằm trong một tập hợp hay không. Nếu là không, thì chắc chắn giá trị đó không nằm trong tập hợp. Nhưng nếu là có, thì chưa chắc phần tử đó đã nằm trong tập hợp (cái này gọi là xác suất dương tính sai).
  • Ưu điểm của Bloom filter là có thể kiểm tra sự tồn tại của 1 giá trị trong một tập hợp cho trước với khoảng thời gian tính toán nhỏ hơn rất nhiều lần so với việc duyệt toàn bộ tập hợp.
  • Kèm theo đó là các nhược điểm liên quan đến false positive - xác suất dương tính sai; chỉ có thể thêm mà không thể xóa phần tử trong tập hợp. Càng thêm nhiều phần tử mới thì false positive càng cao.

Mô hình.

Trong gói tin SYN được gửi tới, ta sẽ lưu lại 4 tham số Source IP (IP nguồn), Destination IP (IP đích), Source PORT (PORT nguồn), Destination PORT (PORT đích). 4 giá trị này sẽ được hash (băm) bằng các hàm băm để có thể tương tác với các Bloom filter.

Sử dụng hệ thống gồm 3 Bloom filter để phát hiện, ngăn chặn các kết nối fake.
  • Bloom filter 1 (BF1): Lưu trữ tất cả các kết nối được request mà server nhận được.
  • Bloom filter 2 (BF2): Lưu trữ các kết nối sạch (đã bắt tay 3 bước đàng hoàng).
  • Bloom filter 3 (BF3): Lưu trữ các kết nối chưa sạch. Lưu ý rằng các kết nối sạch thì ban đầu cũng có 1 gian đoạn ngắn chưa sạch, mà Bloom filter không thể xóa phần tử, do đó có thể hiểu rằng BF3 sẽ chứa tất cả các kết nối đã được theo dõi.

Cơ chế.

Khi nhận được 1 packet SYN, ta sẽ lấy thông tin của 4 tham số trong header của packet đó, rồi thực hiện "băm" ra thành một bộ giá trị G.
Sau đây là pseudo code (mã giả) minh họa cho thuật toán.

if (G ko nằm trong 3BF) {// Đây là kết nối mới, packet đầu tiên.
      BF1.insert(G);
      drop(G);      // Từ chối các kết nối mới
} else if (G có trong BF1) {      // Đây là packet thứ 2
      BF3.insert(G);
      if (G có trong BF2) {      // Đây là kết nối sạch
            pass(G);
      } else if (G có trong BF3) {// Pass hên xui theo xác suất
            pass(G, n-1);
      }
}

Ta có thể thấy rằng BF2 không thể bị tác động trong quá trình thực hiện thuật toán. Điều này là đương nhiên và cần thiết; vì BF2 chứa các kết nối sạch, đã được xác thực bắt tay 3 bước, nên chỉ có hệ thống điều khiển là có khả năng chèn thêm phần tử vào BF2.

Mô tả.

Khi nhận được một packet SYN, sau khi băm dữ liệu cần thiết, ta sẽ kiểm tra kết quả có trong 3 Bloom filter không.
  • Nếu không có trong Bloom filter nào, có nghĩa là đây là packet đầu tiên của một kết nối mới. Ta sẽ bỏ qua nó và lưu lại giá trị băm của nó vào Bloom filter 1.
  • Nếu có trong Bloom filter 2, có nghĩa là đây là kết nối đã được xác thực bắt tay 3 bước. Đương nhiên là sẽ cho qua.
  • Nếu có trong Bloom filter 3 (đương nhiên là chưa có trong Bloom filter 2), có nghĩa là đây là kết nối đã gửi nhiều packet SYN nhưng chưa được xác thực bắt tay 3 bước. Do đó sẽ cho qua với xác suất 1/n; với n là số lần bắt được packet SYN đó.

Kết quả.

  • Gói SYN đầu tiên luôn bị bỏ qua.
  • Gói SYN thứ 2 sẽ được cho qua, và sẽ bị lưu vết nếu không thực hiện bắt tay 3 bước.
  • Nếu tiếp tục có gói SYN thứ 3 trở lên, các gói SYN đó sẽ được cho qua với xác suất 1/n, tỷ lệ nghịch với số lần bắt được gói tin SYN đó.
Ta có thể thấy ngay rằng, Bloom filter 3 chính là nơi lưu trữ thông tin của những "nghi phạm", vì những kẻ này đã gửi packet SYN nhiều lần nhưng lại không thèm xác thực bắt tay 3 bước. Do đó, với cơ chế cho qua một cách hên xui với 1 giá trị xác suất tỷ lệ nghịch với số lần bắt được packet SYN, ta sẽ hạn chế được sự lọt qua của các packet SYN được gửi từ những zombie do handler của hacker.

Đánh giá thêm.

Tuy có ưu điểm về tốc độ xác thực sự tồn tại, nhưng như tôi đã nói, Bloom filter cũng có 1 nhược điểm nản ko kém, đó là không thể xóa 1 phần tử khỏi Bloom filter. Do đó trong thực tế, để có thể sử dụng được các ý tưởng về Bloom filter, ta cần phải tạo kích thước của Bloom filter lên rất rất lớn, cũng như các hàm băm sẽ cần có độ phức tạp và phân tán cao.

Do cấu trúc lớn, yêu cầu tính toán nặng, nên Bloom filter chỉ có thể tự động hạn chế DDoS attack ở tầng giao vận, chứ không thể tự động ngăn chặn tấn công cũng như hạn chế tấn công ở các tầng cao của mô hình mạng OSI. Trong thực tế, người ta không mấy dùng Bloom filter để hạn chế tấn công ở tầng ứng dụng. Nhưng đương nhiên, việc sử dụng Bloom filter ở tầng Giao Vận cũng đã làm giảm số lượng request của dòng thác SYN Flood từ cỡ hàng triệu/s xuống còn hàng trăm/s rồi. Ở tầng ứng dụng, các phần mềm phát hiện, hạn chế, ngăn chăn DDoS attack phải có cấu trúc tinh gọn, tốc độ realtime, hơn cả "bàn thờ"; mà hiện tại thì mình đã implementation thành công ý tưởng thuật toán của Sư Phụ và Trần Sư huynh bên Cục An Toàn Thông Tin bằng C++. Mình sẽ chia sẻ ý tưởng trong các bài đăng tiếp theo.

Cảm ơn các bạn đã dành thời gian đọc hết bài viết!

Nhận xét

  1. Chào anh Cường, em là Trung học KSTNCNTT-K59 đây :D
    Hôm nay em muốn tìm hiểu lại về việc dùng bloom filter chống DDOS, em search google và biết đến blog rất bổ ích của anh.
    Em có 1 câu hỏi là, nếu 3BF không xóa các phần tử mà chỉ thêm vào, thì sau 1 thời gian càng dài, xác suất false positive càng cao, đến 1 lúc nào đó gần như mọi phần tử đều được kết luận là có trong các BF, thì đến lúc đó BF đâu còn ý nghĩa gì nữa, đúng không anh? Không lẽ sẽ định kì reset các BF sau 1 khoảng thời gian nào đó?
    Em nghĩ có thể dùng CTDL nào đó như Set thay cho BF, có thể chèn, xóa, kiểm tra với thời gian nhanh, bộ nhớ có vẻ sẽ nhiều hơn 1 chút, anh thấy sao?

    Trả lờiXóa
    Trả lời
    1. Chào em! Cảm ơn em! Lâu rồi anh ko viết bài nên cũng ko để ý có cmt.

      Về câu hỏi của em thì anh sẽ giải đáp kèm 1 vài giải thích như sau:
      - Đúng, BF ko hỗ trợ xóa, chỉ có chèn thêm và chèn mãi, do đó cần có tính toán trước khi thiết kế BF.
      - BF không đầy lên theo thời gian, mà đầy lên theo số lượng case - bộ kết nối (sourceIP, destIP, sourcePort, destPort).
      - Việc thiết kế BF phải được hoạch định ngay từ lúc dự trù ban đầu. Tức là nếu em muốn kiểm soát tầm 1 triệu case, sử dụng 6 hàm băm, thì các BF của em phải có kích thước ít nhất 8660000bit thì mới có thể có xác suất gặp false positive là 1.6%.
      - Điều này vẫn tiết kiệm hơn về mặt lưu trữ nếu em dùng set. Anh cứ lấy đơn cử em dùng bit để lưu 1 bộ kết nối đi, mỗi địa chỉ IP em đã mất 32bit (IPv4), mỗi port em mất thêm 16bit (TCP hỗ trợ tối đa 65536 port), thì 1 case em đã mất 128bit, 1 triệu case em sẽ mất 128 triệu bit, quá lớn so với 8.66 triệu bit nếu dùng BF.
      - Còn chưa kể đến việc verify kết quả. Nếu dùng BF, em sẽ đưa ra kết luận trong thời gian O(6) (6 hàm băm). Nhưng nếu dùng set, em phải mất O(128 triệu) (đúng bằng kích thước của set) (anh cũng ko biết em sẽ sắp xếp set theo kiểu như nào nên cứ lấy max size vậy). Vậy nên nếu xét về kiểm tra nhanh thì các CTDL thuần lập trình sẽ ko nhanh bằng BF được; bộ nhớ cũng tốn kém nhiều chút đó, chứ ko 1 chút đc (1 số nguyên interger 4bytes = 32bit rồi mà).

      Mấy con số kia là anh tính rõ giá trị ra cho em dễ hình dung thôi, còn nếu em muốn khảo sát về mặt công thức toán học thì có thể tham khảo thêm tài liệu này, cũng là Thầy bảo lớp bọn anh đọc thôi! :D
      https://drive.google.com/file/d/1uMJaudWSj-hH70K1lJfKxbIJNNUJkzk5/view?usp=sharing

      Xóa
    2. à quên nhẩm sai, đoạn kia là 96bit, không phải 128bit nhé. Nên các đoạn sau cũng chỉ là 96 triệu, ko phải 128 triệu nhé! :D

      Xóa

Đăng nhận xét

Bài đăng phổ biến từ blog này

Trên con đường tu đạo luôn cực kỳ theo đuổi!

C++ Con trỏ (Pointer) toàn thư: Phần 4: Con trỏ "đa cấp". Đánh nhau bằng con trỏ.

Quan hệ giữa các phân phối xác suất thông dụng nhất: Beta và Dirichlet không giống Gaussian!