HTTP/2 Rapid Reset Attack


Một trong những vấn đề bảo mật được nhắc đến nhiều nhất gần đây là HTTP/2 Rapid Reset Attack, được dùng để tạo ra các cuộc tấn công DDOS, với báo cáo lớn nhất là 394 triệu yêu cầu được gửi đến mỗi giây.

Tui nhân dịp này tranh thủ giải thích về lỗi bảo mật này, giúp các bạn hiểu thêm về giao thức HTTP, và hiểu về code của dự án Mini-Web-Server dễ dàng hơn.

Cách một request được xử lý trong HTTP/1.1

Khi một client gửi request đến một HTTP server, các bước thực hiện sẽ như sau:

– Mở kết nối TCP/IP đến HTTP server.

– Gửi request.

– Server phân tích request.

– Server xử lý request và trả về response.

– Client đọc response và xử lý kết quả.

Mỗi một vòng như vậy gọi là 1 round trip, client sẽ phải chờ cho đến khi server hoàn thành việc xử lý và trả về kết quả. Trong lúc server xử lý request, ví dụ như đọc file, cập nhật database… thì các phần khác cũng phải dừng lại. Client muốn gửi request kế tiếp thì phải chờ cho đến khi request hiện tại xử lý xong, do vậy không tận dụng hết tài nguyên của cả client và server.

HTTP/2 đã giải quyết vấn đề này bằng cách nào?

HTTP/2 cho phép cắt nhỏ các request và response thành nhiều đoạn gọi là các frame, các frame này có thể được gửi xen kẽ với nhau, cho phép client và server gửi và nhận nhiều request/response đồng thời trên một connection. Ví dụ thay vì client phải gửi 2 request trong 2 round trip, client có thể gửi trong một round trip, bởi server hoàn toàn có khả năng xử lý 2 request (đọc file, cập nhật database) đồng thời. Khả năng gửi nhận đồng thời nhiều request trên cùng một connection bằng cách chia thành frame chính là ưu điểm lớn nhất của HTTP/2 so với HTTP/1.1.

Để giới hạn việc sử dụng tài nguyên, thông thường HTTP/2 server sẽ hạn chế số lượng request được gửi đồng thời trên một connection, ví dụ khoảng 100, nếu nhiều hơn thì client cần tạo một connection mới, điều này giúp server giảm sự bận rộn trên mỗi connection cũng như kiểm soát được lượng kết nối tối đa mà một client có thể làm việc (với server).

Ngoài các tính năng trên, HTTP/2 còn cho phép client gửi frame RST_STREAM để yêu cầu hủy request đang gửi, trên lý thuyết, server nhận được sẽ hủy các frame trước đó của request, giải phóng tài nguyên liên quan tới request, đồng thời ghi nhận số lượng request hiện tại trên connection giảm xuống. Hacker lợi dụng điều này để liên tục gửi các request frame và ngay lập tức gửi tiếp RST_STREAM frame, bởi sau khi gửi RST_STREAM, client được phép gửi tiếp một request frame khác, và lại hủy, cứ như vậy khiến server quá tải.

Các bạn có thể đặt câu hỏi rằng ngay khi client hủy request thì server cũng hủy, vậy làm sao server quá tải được? Vấn đề là: việc tạo ra một request đơn giản hơn rất nhiều so với việc xử lý request đó. Các bạn có thể thấy trong Mini-Web-Server, khi nhận một kết nối mới, server phải phân bổ tài nguyên, đọc vào gói tin, phân tích các gói tin, ngay cả RST_STREAM frame, vốn để hủy request, bạn cũng cần phân phối tài nguyên, đọc, phân tích dữ liệu rồi sau đó mới biết đó là lệnh hủy, chưa kể trường hợp nếu server này là một reversed proxy, nó sẽ cần truy vấn vào server backend, việc hủy các request như vậy còn phức tạp hơn nữa.

Lấy một ví dụ tương tự: khi bạn kết nối vào một cơ sở dữ liệu và gửi một câu lệnh truy vấn, những gì server phải làm có thể phức tạp và tốn tài nguyên gấp hàng ngàn, thậm chí hàng triệu lần so với việc bạn tạo ra câu SQL và gửi đến server. Sự bất cân xứng giữa việc sử dụng tài nguyên ở hai bên khiến cho việc tấn công DDOS vào các máy chủ HTTP/2 dễ dàng hơn.

Hiện tại nhiều HTTP server đã cập nhật bản vá chống lại lỗi này, nhưng những nhà quản trị có thể bị chậm chân so với kẻ tấn công (một lần nữa, việc cập nhật vá lỗi phức tạp hơn nhiều so với việc tấn công).

Một tin mừng là Mini-Web-Server không bị dính lỗi này, đơn giản là nó chưa hỗ trợ HTTP/2, vậy nên nếu có bạn nào tấn công vào http://www.mini-web-server.com bằng cách này sẽ không có tác dụng 😁.

1 giây quảng cáo, mã nguồn Mini-Web-Server tại đây, hiện mình đang implement MVC trong branch mini-mvc: https://github.com/daohainam/mini-web-server

Note: Các bạn có thể xem commit liên quan đến gói sửa lỗi của Kestrel web server (máy chủ web trong ASP.NET) tại đây: https://github.com/dotnet/aspnetcore/commit/6c6d3049d098011476d51dfaaf8c2fa2b7da7638?fbclid=IwAR2MMdR-ilJtLxqnbqiL_AZLCucMJXAO6WIjByXs9KLV_kcbzL8LnvXH_Rs

Leave a comment