ModSecurity với bộ quy tắc OWASP CRS được bật trong mười phút và bị tắt ba ngày sau đó — sau khi bài viết trong khu quản trị không lưu được, việc tải tệp lên hỏng, và một khách hàng không đặt được đơn vì địa chỉ của họ có dấu nháy đơn. Kết luận «WAF cản trở công việc» tự nó nảy ra, và nó sai: gần như tất cả những lần chặn ấy đều chữa được bằng ba bốn ngoại lệ chính xác, và toàn bộ mẹo nằm ở chỗ tìm ra chúng cho đúng.

Đừng bật chế độ chặn ngay lập tức

Tuần đầu chỉ để quan sát. Trong /etc/modsecurity/modsecurity.conf:

SecRuleEngine DetectionOnly

Ở chế độ này WAF ghi lại mọi thứ mà nó lẽ ra sẽ chặn và không chặn gì cả. Một tuần lưu lượng thật — bao gồm công việc của chính bạn trong khu quản trị, việc tải ảnh lên và việc đặt đơn hàng — cho ra danh sách các dương tính giả có thật thay vì giả định. Chuyển sang On chỉ có ý nghĩa khi danh sách đó đã được xử lý xong.

CRS hoạt động thế nào: không phải một quy tắc mà là tổng

Đây là chìa khóa cho mọi thứ tiếp theo. CRS gần như không bao giờ chặn một yêu cầu bằng một quy tắc duy nhất. Mỗi quy tắc kích hoạt sẽ cộng điểm bất thường vào yêu cầu, và việc chặn xảy ra khi tổng vượt ngưỡng. Vì vậy trong nhật ký không phải một dòng mà là vài dòng, và dòng cuối cùng là quy tắc có mã 949110 — chính là cái cộng tổng.

Hệ quả thực tiễn: ngoại lệ phải tạo cho quy tắc đã cộng điểm, chứ không phải cho 949110. Tắt quy tắc cộng tổng là tắt cả bộ, và WAF chỉ còn là một dòng trong tệp cấu hình.

Hệ quả thứ hai là mức paranoia. Mặc định nó bằng một, và đó là lựa chọn đúng. Mức 2 và 3 thêm những quy tắc vốn dĩ tạo ra dương tính giả trên các trang web bình thường, và chỉ nên bật khi mức một đã được tinh chỉnh hoàn toàn.

Tìm quy tắc gây lỗi

Mọi thứ cần thiết đều nằm trong nhật ký audit (/var/log/modsec_audit.log) và trong nhật ký lỗi của máy chủ web. Hãy tìm theo thời điểm bị chặn:

sudo grep -o 'id "[0-9]*"' /var/log/modsec_audit.log | sort | uniq -c | sort -rn | head

Đó là danh sách tần suất các quy tắc đã kích hoạt. Sau đó với một mã cụ thể, hãy xem chính xác cái gì đã kích hoạt nó:

sudo grep -A5 'id "942100"' /var/log/modsec_audit.log | head -40

Cần ba thứ: mã quy tắc, tên tham số (ARGS:content, ARGS:comment) và đường dẫn của yêu cầu. Ngoại lệ được dựng từ chúng.

Những nghi phạm quen thuộc

Danh sách này lặp lại từ trang này sang trang khác:

  • 942100 — SQL injection. Kích hoạt với các trường văn bản có nội dung dài: thân bài viết, mô tả sản phẩm, bình luận. Dấu nháy, dấu ngoặc và các từ như select nằm trong văn xuôi bình thường trông đáng ngờ với bộ phát hiện;
  • 941100 và họ 941xxx — XSS. Chúng đến cùng với trình soạn thảo trực quan: thẻ HTML trong một trường chính là toàn bộ ý nghĩa hoạt động của nó;
  • 920420 — Content-Type không được phép. Làm hỏng API và việc tải tệp lên: bộ kiểu được phép mặc định rất hẹp, và ở các phiên bản cũ application/json thậm chí không có trong đó;
  • 913100 — trình quét theo User-Agent. Bắt cả những công cụ hợp lệ cùng với trình quét: giám sát tính khả dụng, curl trong script của chính bạn;
  • 200002, 200004 — lỗi đọc thân yêu cầu. Chúng thường không có nghĩa là tấn công mà là vượt giới hạn kích thước thân, tức là tải lên một tệp lớn.

Ba cách tạo ngoại lệ

Theo thứ tự thô bạo tăng dần. Tất cả đều đi vào tệp của riêng bạn (ví dụ /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES.conf) chứ không phải vào các tệp của chính CRS: bộ quy tắc có cập nhật và các chỉnh sửa của bạn sẽ biến mất cùng với nó.

Gỡ một tham số khỏi một quy tắc. Lựa chọn chính xác nhất, và là cái nên hướng tới:

SecRuleUpdateTargetById 942100 "!ARGS:content"

Tắt quy tắc chỉ trên một đường dẫn. Đúng lúc khi một trang cụ thể gây ồn — trình soạn thảo, việc nhập dữ liệu, biểu mẫu đánh giá:

SecRule REQUEST_URI "@beginsWith /admin/post" \
    "id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"

Tắt hẳn quy tắc. Biện pháp cuối cùng và gần như luôn là dấu hiệu cho thấy nguyên nhân chưa hề được tìm ra:

SecRuleRemoveById 942100

Khác biệt giữa cách thứ nhất và cách thứ ba là rất lớn. Ở trường hợp đầu, một trường — thân bài viết — thôi không bị kiểm tra SQL injection bởi một quy tắc; ở trường hợp hai và ba, cả trang web thôi không được kiểm tra. Khác biệt về công sức là khoảng năm phút.

Sau khi sửa, hãy kiểm tra cấu hình và nạp lại nhẹ nhàng:

sudo apachectl configtest && sudo systemctl reload apache2
sudo nginx -t && sudo systemctl reload nginx

Trình tự tiết kiệm được một tuần

  1. Một tuần ở DetectionOnly với công việc bình thường, gồm cả trang web, khu quản trị và việc tải lên.
  2. Danh sách tần suất các quy tắc từ nhật ký audit. Đi từ trên xuống — ba bốn cái đầu chiếm chín mươi phần trăm tiếng ồn.
  3. Với mỗi cái: xác định là tham số nào và trên trang nào. Tạo ngoại lệ theo tham số, không phải theo quy tắc.
  4. Và chỉ đến lúc này mới SecRuleEngine On.
  5. Mỗi tháng một lần hãy ghé xem các lần chặn: trang web đã thay đổi, nên có những dương tính giả mới.

Chính ở điểm cuối này mọi thứ thường sụp đổ: nhật ký audit là hàng gigabyte văn bản và chẳng ai đọc nó bằng tay. Ý nghĩa của một bảng gom lại là để danh sách quy tắc đã kích hoạt, các yêu cầu bị chặn và bộ quy tắc đang hoạt động hiện ra trước mắt bạn thay vì phải moi ra bằng grep. Trông ra sao — xem ở trang demo bên dưới.