Cloudflare đăng một bài ngày 29/9 mô tả thứ mà hầu hết team bảo mật hay nói muốn xây nhưng hiếm khi thật sự ra được sản phẩm: một LLM tấn công chính WAF của họ, đọc response, rồi biến đổi payload cho lần thử tiếp theo. Không có quyền truy cập rule của WAF. Không có chi tiết nội bộ nào cả. Chỉ có một black box và một model tự quyết định thử gì tiếp.
Con số cụ thể: 1.107 lần biến đổi payload trên 45 kịch bản, sáu nhóm tấn công — XSS, SQL injection, command injection, SSRF, path traversal, Log4j. 558 request bị chặn. 49 phát hiện cần vá, và 48 trong số 49 đó dồn vào đúng hai nhóm: command injection và SSRF. Ba detection mới được đưa vào managed ruleset, trong đó có một cái chuyên bắt payload SSRF ẩn sau định dạng IP số không chuẩn. Mọi phát hiện do AI sinh ra đều qua review của con người trước khi triển khai — Cloudflare nói rõ điều này, và mình tin, vì output của model một mình không phải là một phát hiện, nó chỉ là một tuyên bố cần kiểm chứng.
Kết quả tốt. Nhưng chưa phải phần hay.
Con số mà ai cũng sẽ lướt qua
XSS, path traversal, SQL injection, Log4j — coverage tốt, gần như sạch. Command injection và SSRF — 48 lỗ hổng dồn vào hai nhóm đó. Cùng ngân sách test, cùng model, cùng vòng lặp. Khác biệt không nằm ở công sức bỏ ra. Nó nằm ở chỗ hai nhóm đó tình cờ có nhiều không gian biến đổi thật sự mới (mẹo encoding, cách biểu diễn IP số, tổ hợp ký tự đặc biệt của shell mà dữ liệu huấn luyện của WAF chưa từng thấy), còn bốn nhóm kia thì gần như hết chỗ để gây bất ngờ.
Đây là phần đáng lo nếu bạn đang vận hành một bộ test bảo mật: phần lớn chúng ta vẫn báo cáo coverage bằng số lần thử, số kịch bản, hoặc “tuần này chạy 1.107 payload lên API”. Con số đó gần như không nói lên được gì về việc bạn có tìm ra đúng lỗ hổng quan trọng hay không. Dữ liệu của chính Cloudflare cho thấy tỷ lệ phát hiện không đồng đều giữa các nhóm — nó dồn gần hết vào hai trên sáu nhóm. Nếu công sức test của bạn cũng dàn đều theo nhóm, còn rủi ro thật thì không, bạn đang phân bổ sai tài sản quý giá nhất — dù đó là lượt gọi model hay giờ công của tester.
Một harness “mù” trước tỷ lệ phát hiện trông như thế nào
Phiên bản ngây thơ của vòng lặp này chỉ đơn giản xoay vòng hoặc chọn ngẫu nhiên nhóm:
categories = ["xss", "sqli", "cmdi", "ssrf", "lfi", "log4j"]
def naive_next_category(attempt_count):
return categories[attempt_count % len(categories)]
Mỗi nhóm nhận đúng một phần ngân sách bằng nhau, mãi mãi, bất kể nhóm nào đang thật sự sinh ra phát hiện. Đó là cách bạn tiêu 900 trong 1.107 lần thử để xác nhận lại những thứ vốn đã biết là ổn.
Một phiên bản biết ưu tiên theo đa dạng sẽ theo dõi tỷ lệ phát hiện theo từng nhóm, rồi tái phân bổ trọng số về phía nhóm đang sinh tín hiệu, nhưng không bao giờ để một nhóm im ắng bị bỏ đói hoàn toàn — vì “im ắng” và “đã giải quyết xong” không phải là một:
import random
def next_category(history):
# history: {category: {"attempts": int, "findings": int}}
weights = {}
for cat, stats in history.items():
rate = stats["findings"] / max(stats["attempts"], 1)
# trọng số sàn giữ mọi nhóm luôn sống; không nhóm nào về 0
weights[cat] = max(rate, 0.05)
total = sum(weights.values())
r = random.uniform(0, total)
upto = 0
for cat, w in weights.items():
upto += w
if upto >= r:
return cat
return list(history.keys())[-1]
Đây là một thuật toán bandit năm phút, chẳng phải nghiên cứu gì mới — điểm quan trọng không phải là đoạn code, mà là số liệu thô của Cloudflare chính là lý lẽ rõ ràng cho việc tại sao bạn nên bận tâm viết nó ra. Nếu hai trên sáu nhóm sinh ra 98% phát hiện, một harness test không biết điều đó đang bỏ phí 900 lần thử còn lại vào việc gần như chẳng đóng góp gì.
Bước review của con người không phải là chi tiết phụ
Mình muốn dừng lại ở một chi tiết Cloudflare nhắc gần như lướt qua: mọi phát hiện do AI sinh ra vẫn phải qua tay con người trước khi được tính là một phát hiện thật. Đáng lẽ dễ dàng bỏ qua dòng đó trong bài viết — phần automation mới là phần ấn tượng, bước review nghe có vẻ chỉ là thủ tục nhàm chán. Nó không nhàm chán, đó là toàn bộ lý do khiến con số 49 đáng tin. Một LLM tấn công vào black box sẽ tạo ra rất nhiều request trông như bypass thành công mà thật ra không phải — một WAF trả về 200 vì payload tình cờ trúng một route vốn chưa từng được bảo vệ ngay từ đầu không phải là lỗ hổng, đó là một false positive được trình bày đẹp đẽ. Bỏ qua bước review để tiết kiệm thời gian thì số phát hiện tăng lên trong khi tình trạng bảo mật thật sự vẫn y nguyên, hoặc tệ hơn vì có người đi “vá” một lỗi chưa từng tồn tại.
Chỗ mình sẽ áp dụng ngay ngày mai
Không chỉ test WAF. Bất kỳ nơi nào team bạn chạy scan bảo mật hoặc chất lượng tự động với ngân sách cố định — fuzzing, quét dependency, kể cả bot review code bằng LLM đi săn bug — nhiều khả năng cũng báo cáo kết quả phẳng lì như con số thô của Cloudflare, nếu họ dừng lại ở “1.107 lần thử, 49 phát hiện” rồi coi như xong. Hãy tách số liệu của chính bạn theo từng nhóm trước khi tin vào con số tổng. Nếu một nhóm im lặng, đó hoặc là tin tốt, hoặc là dấu hiệu harness của bạn chưa bao giờ học được cách nhìn vào đó — và nhìn từ bên ngoài, hai trường hợp đó giống hệt nhau cho tới khi bạn thật sự đi kiểm tra. Và luôn giữ con người trong vòng lặp với bất kỳ thứ gì model gọi là một phát hiện, không phải vì model không đáng tin, mà vì “trông như bypass” và “là bypass thật” là hai tuyên bố khác nhau, và chỉ một trong hai đáng để mở ticket.