Làm dev đau lưng

Bài toán Rate Limiting

Làm thế nào để giới hạn số lượng truy cập (cả đọc và ghi) vào hệ thống?
Chà, với những công nghệ hiện nay thì không khó để trả lời ngay là dùng một dịch vụ bên thứ ba như AWS, hay Cloudflare… nhưng mà với vai trò một dev quèn, nếu bắt buộc phải gõ thì mình sẽ gõ nó như nào? Suy nghĩ một hồi lâu, mình đẻ ra được hai trường hợp:

Trường hợp 1: Người dùng đã đăng nhập vào hệ thống.
Cái này có vẻ dễ thở, vì đã đăng nhập nên có thể dựa vào id người dùng để giới hạn lượt truy cập. Kết hợp cache cùng mới một vài thuật toán như fixed window, leaky bucket, sliding window, hay token bucket,… tùy trường hợp mà xử lý. À, mình có gõ vài đường ở đây bạn tham khảo.

Trường hợp 2: Người dùng chưa đăng nhập vào hệ thống
Đoạn này khoai, đây là public requests mình không thể control được, gặp users nhỏ thì không nói, users tầm cỡ influencer mà chia sẻ link app lên network của họ thì không khác gì server đang dính chưởng Distributed Denial-of-Service (DDoS) attack. Vậy thì phải làm sao?

Mình nghĩ có một vài cách tất nhiên là đi cùng với trade-offs.

  1. Giới hạn cả API endpoint
    Cấp N requests mỗi M thời gian, 10 000 requests mỗi 15 phút chẳng hạn. Cách này lại gây ra một vấn đề đó là nó quá cứng nhắc, nếu trong khung thời gian vượt xa N requests thì phần còn lại sẽ bị blocked. Và phải làm sao nếu attackers fake cả N requests đó làm cho real users không thể truy cập?

  2. Giới hạn theo từng địa chỉ IP
    Okay, thay vì đặt giá trị cứng cho cả endpoint thì mình chuyển sang từng IP, mỗi IP có thể truy cập N requests / M thời gian, ví dụ 100 requests mỗi 15 phút. Nhìn sơ qua thì cách này ổn áp đúng không? Nhưng ta quên rằng attackers cũng có thể fake IP mỗi lần truy cập -> Quay về con số không 😆

  3. Đập thêm tiền vào hạ tầng
    Bí quá chạy đi xin tiền công ty thôi, just kidding =)))

  4. Honeypot
    Hầu hết các bot đơn giản sẽ tìm kiếm theo common patterns, labels, ids, attributes, required fields,… sau đó là fake dữ liệu và gửi lên server. Ý tưởng: Thêm một trường ẩn mà người dùng sẽ không điền vào nhưng bot có thể điền, sau đó từ chối form nếu trường này có dữ liệu:

<form action="/submit" method="POST">
    <!-- Real fields -->
    <input type="text" name="namesljf" placeholder="Your Name" required>
    <input type="email" name="emailkjkl" placeholder="Your Email" required>

    <!-- Honeypot Field -->
    <input type="email" name="email" placeholder="Your Email" style="display: none;">

    <input type="submit" value="Submit">
</form>

Honeypot khá ổn áp nhưng cũng không thể cover cho tất cả các trường hợp, chỉ nên là một lớp bảo mật thêm vào. Nhỡ có bọn attackers nhà giàu bật cả E2E servers, chạy automation test giúp app mình luôn thì…

  1. Captcha
    Cách này thì khỏi lo bọn bot các kiểu rồi, cơ mà về mặt UX quá chán nên cũng hốt luôn real users 🤡.
I'm not a robot.
  1. Áp dụng thuật toán blockchain
    Có nhiều loại nhưng trong bài này mình sẽ lấy Proof of work (PoW) làm ví dụ. Ý tưởng: Để truy cập, người dùng phải thực hiện việc giải mã hash với độ khó N. Sau đó submit lời giải lên server để xác nhận lại. Bằng cách này ta có thể quan sát để điều chỉnh độ khó của mã hash, tác động trực tiếp đến thời gian truy cập của người dùng.
  • Bots sẽ mất một khoảng thời gian nhất định để giải mã.
  • Nếu là người dùng thật sự, việc giải mã không phải là một vấn đề khó.
  • Việc xác nhận lại lời giải của người dùng có chi phí khá rẻ so với việc thực hiện một Write request.

Điều này giúp endpoint vẫn có thể hoạt động bình thường, đồng thời giảm tải trên toàn bộ server. Mình cũng có mần một POC nhỏ ở đây.

Túm cái váy lại là mình viết cho vui thôi, chứ hôm phỏng vấn mình tạch =))) Buồn quá nên về nghiên cứu lại, hy vọng lần sau bị hỏi còn biết đường chém gió.

Tham khảo thêm: