Bảo vệ web bằng ModSecurity WAF và OWASP CRS trên nginx

Web của bạn đang chạy tốt trên VPS, nhưng bạn có biết mỗi ngày có bao nhiêu request độc hại đập vào nginx không? Các bot quét lỗ hổng tự động tìm kiếm `wp-login.php`, `.env`, hay thử SQL injection ở mọi tham số. ModSecurity là tầng chặn đầu tiên, đứng trước mã nguồn ứng dụng, lọc bỏ những request này. Bài này hướng dẫn cài ModSecurity WAF cùng bộ luật OWASP CRS trên nginx chạy Ubuntu 24.04, cấu hình ở mức "chặn" và theo dõi log để tránh chặn nhầm người dùng thật.
Kết quả sau khi hoàn thành: nginx chạy thêm module ModSecurity, bộ luật OWASP CRS được nạp, các request tấn công bị trả về mã 403. Toàn bộ thao tác thực hiện qua SSH với quyền root hoặc user có `sudo`. Hướng dẫn dùng bản nginx cài từ kho lưu trữ Ubuntu mặc định (1.24), không dùng bản tự biên dịch.
Yêu cầu trước khi bắt đầu
- Một VPS chạy Ubuntu 24.04 LTS, có quyền `root` hoặc user nằm trong nhóm `sudo`. Nếu chưa có máy, bạn có thể thuê VPS Linux tại thueVPS và cài lại OS Ubuntu 24.04 qua panel.
- Nginx đã cài sẵn chạy nền tảng: `sudo apt install nginx`.
- Một website (hoặc server block) đang hoạt động trên nginx để áp cấu hình WAF. Nếu chưa có, hãy đọc bài VPS cho người mới: dựng website đầu tiên trên Ubuntu 24.04.
- Lưu ý quan trọng: ModSecurity hoạt động ở tầng HTTP, nó khác hoàn toàn với firewall tầng mạng như ufw. Bạn cần cả hai, không phải một trong hai.
Nếu bạn dùng nginx cài qua bản tự biên dịch từ nginx.org hoặc qua Docker image chính thức, cách cài libmodsecurity3 sẽ khác. Bài này chỉ áp dụng cho bản nginx từ kho Ubuntu.
Vì sao nên đặt ModSecurity trước ứng dụng
Phần lớn ứng dụng web đều có lỗ hổng, đặc biệt là các website dùng mã nguồn mở: WordPress, Joomla, hoặc các ứng dụng tự viết. Việc vá lỗi phụ thuộc vào tốc độ ra bản vá của nhà phát triển. Trong khoảng thời gian chờ vá, kẻ tấn công có thể lợi dụng lỗ hổng để đánh cắp dữ liệu. WAF là tầng phòng thủ chủ động: nó chặn các mẫu tấn công đã biết ngay tại nginx, không cần đợi ứng dụng được vá.
OWASP CRS (Core Rule Set) tập hợp hơn 200 luật phát hiện các kỹ thuật tấn công phổ biến: SQL injection, cross-site scripting (XSS), Local File Inclusion, Remote Code Execution, chiếm quyền máy chủ, và cả các bot tự động quét web. Những luật này được cộng đồng bảo mật duy trì và cập nhật thường xuyên, tương thích chuẩn ModSecurity v3.
Điều quan trọng cần hiểu: ModSecurity không thay thế việc vá lỗ hổng. Nó là lớp giảm thiểu rủi ro, giúp bạn có thời gian vá lỗi mà không bị khai thác. Nếu website của bạn là mục tiêu có chủ đích, vẫn phải sửa code cho đúng.
Bước 1 - Cài đặt ModSecurity và nginx module
Trên Ubuntu 24.04, các gói liên quan tới ModSecurity đều có sẵn trong kho lưu trữ. Bạn không cần biên dịch thủ công, tiết kiệm được nhiều thời gian. Chạy lệnh sau để cập nhật danh sách gói và cài đặt:
sudo apt update
sudo apt install libmodsecurity3 libnginx-mod-http-modsecurity -y
Gói `libmodsecurity3` là thư viện lõi ModSecurity v3, còn `libnginx-mod-http-modsecurity` là module nginx để gọi thư viện này. Sau khi cài xong, kiểm tra module đã được nạp vào nginx hay chưa:
nginx -V 2>&1 | grep modsecurity
Kết quả trả về có dạng `--add-dynamic-module=/build/nginx-.../debian/modules/...` nghĩa là module đã được biên dịch dạng động. Bạn cần kích hoạt nó trong file cấu hình chính trước khi dùng:
sudo nano /etc/nginx/nginx.conf
Thêm dòng sau vào đầu khối `events { ... }` (vị trí bất kỳ ngoài khối `http` hoặc `server`):
load_module modules/ngx_http_modsecurity_module.so;
Kiểm tra lại cấu hình và nạp lại nginx:
sudo nginx -t
sudo systemctl reload nginx
Kết quả mong đợi ở lệnh `nginx -t` là dòng `syntax is ok` và `test is successful`. Nếu bạn gặp lỗi không tìm thấy file module, đường dẫn có thể khác. Hãy chạy `sudo find / -name "ngx_http_modsecurity_module.so" 2>/dev/null` để tìm đúng vị trí.
Bước 2 - Cài đặt bộ luật OWASP CRS
ModSecurity chỉ là khung xử lý, không có luật thì nó không chặn được gì. OWASP CRS là bộ luật chuẩn, bạn tải về giải nén vào thư mục riêng để tiện quản lý cập nhật:
sudo mkdir -p /etc/nginx/modsecurity/
cd /tmp
curl -LO https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.11.0.tar.gz
sudo tar -xzf v4.11.0.tar.gz -C /etc/nginx/modsecurity/ --strip-components=1
sudo mv /etc/nginx/modsecurity/crs-setup.conf.example /etc/nginx/modsecurity/crs-setup.conf
Bộ luật cần file cấu hình chính. Bản OWASP CRS 4.x cung cấp sẵn file `crs-setup.conf.example`, bạn đổi tên thành `crs-setup.conf` để nginx nạp. Tiếp theo, tạo file cấu hình ModSecurity cho riêng nginx:
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/nginx/modsecurity/modsecurity.conf
sudo nano /etc/nginx/modsecurity/modsecurity.conf
Trong file vừa mở, tìm dòng `SecRuleEngine DetectionOnly` và sửa thành `On`. Đây là chế độ hoạt động: `On` là chặn thật sự, `DetectionOnly` chỉ ghi log. Với bản cài đầu tiên, bạn có thể để `DetectionOnly` vài ngày để quan sát, sau đó chuyển sang `On`. Ở đây mình chuyển thẳng `On` để đúng mục đích bài.
SecRuleEngine On
Lưu file và đóng trình soạn thảo.
Bước 3 - Cấu hình ModSecurity hoạt động với OWASP CRS
Bây giờ bạn cần file cấu hình để nginx biết đường dẫn tới bộ luật. Tạo file mới:
sudo nano /etc/nginx/modsecurity/modsecurity_crs.conf
Nội dung file như sau:
Include /etc/nginx/modsecurity/modsecurity.conf
Include /etc/nginx/modsecurity/crs-setup.conf
Include /etc/nginx/modsecurity/rules/*.conf
Dòng `Include` thứ ba nạp toàn bộ luật từ thư mục `rules`. Kiểm tra thư mục `rules` có tồn tại và chứa file luật không:
ls -l /etc/nginx/modsecurity/rules/ | head -20
Kết quả phải có các file như `REQUEST-901-INITIALIZATION.conf`, `REQUEST-942-APPLICATION-ATTACK-SQLI.conf`. Nếu thư mục trống, bạn đã giải nén sai thư mục. Quay lại Bước 2 kiểm tra.
Nếu máy chủ của bạn có RAM thấp (dưới 2GB), bạn nên chủ động tinh chỉnh để tránh nginx tăng đột biến bộ nhớ. Cài thêm gói hỗ trợ kiểm tra bộ nhớ và xem lại hướng dẫn cấu hình swap và tối ưu bộ nhớ cho VPS RAM thấp trước khi bật WAF.
Bước 4 - Kích hoạt ModSecurity cho website
ModSecurity hoạt động theo từng server block hoặc từng location. Bạn quyết định phạm vi áp dụng. Mở file cấu hình server block của website, thường nằm tại `/etc/nginx/sites-available/`:
sudo nano /etc/nginx/sites-available/example.com
Thêm hai dòng sau vào bên trong khối `server { }`:
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity_crs.conf;
Vị trí bạn đặt quyết định phạm vi hoạt động. Đặt trong khối `server` thì toàn bộ website được bảo vệ. Đặt trong khối `location` thì chỉ riêng đường dẫn đó. Với hầu hết trường hợp, đặt ở khối `server` là đơn giản và đầy đủ.
Kiểm tra cấu hình và nạp lại nginx:
sudo nginx -t
sudo systemctl reload nginx
Đến đây ModSecurity đã chạy ở chế độ chặn. Hãy thử một request tấn công SQLi kinh điển:
curl -I "http://example.com/?id=1%20OR%201=1"
Nếu cấu hình đúng, bạn nhận mã trả về `403 Forbidden`. Request bình thường vẫn trả về `200 OK`:
curl -I "http://example.com/"
Nếu website của bạn chạy Nginx làm reverse proxy cho ứng dụng, cách cấu hình tương tự. Hãy xem ví dụ trong bài cài đặt và tối ưu nginx làm reverse proxy trên Ubuntu 24.04 để biết vị trí đặt đúng.
Bước 5 - Theo dõi log và xử lý chặn nhầm
ModSecurity ghi log mọi request bị chặn hoặc ở mức phát hiện. Đường dẫn log mặc định là `/var/log/modsec_audit.log`. Theo dõi log theo thời gian thực:
sudo tail -f /var/log/modsec_audit.log
Khi có cảnh báo hoặc chặn, log có cấu trúc khá dài. Phần quan trọng nằm ở đoạn có thẻ `[id "942100"]` và mô tả. Ví dụ:
Message: Warning. Pattern match "(?i:(?:[...]))" at ARGS:id. [file "/etc/nginx/modsecurity/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [id "942100"]
Phần `[id "942100"]` cho biết luật nào đã kích hoạt. `ARGS:id` cho biết tham số bị nghi ngờ, ở đây là tham số `id` trong query string.
Trong quá trình vận hành thực tế, bạn sẽ gặp trường hợp chặn nhầm người dùng hợp lệ. Nguyên nhân thường gặp: người dùng nhập nội dung có chứa chuỗi giống mẫu tấn công (ví dụ bình luận chứa từ khóa SQL), hoặc ứng dụng dùng tham số chứa nội dung đặc biệt. Cách xử lý: xác định đúng luật từ log, rồi tạo luật loại trừ (exclusion) cho riêng trường hợp đó.
OWASP CRS có sẵn cơ chế REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf để bạn thêm luật loại trừ. Ví dụ bỏ qua kiểm tra cho một tham số cụ thể:
SecRuleUpdateTargetById 942100 "!ARGS:content"
Dòng này nói với ModSecurity: luật 942100 (SQLi) không áp dụng cho tham số `content`. Bạn thêm dòng này vào cuối file `crs-setup.conf` hoặc file riêng, rồi reload nginx. Đừng tắt cả luật, chỉ loại trừ đúng tham số gây lỗi.
Bước 6 - Tinh chỉnh hiệu năng và bảo mật bổ sung
ModSecurity xử lý mọi request đi qua, nên có chi phí hiệu năng nhất định. Với VPS có cấu hình nhỏ (2GB RAM trở xuống), bạn nên cân nhắc chỉ bật WAF cho các location nhạy cảm như `wp-login.php`, `/admin`, thay vì toàn bộ website. Cách làm: đặt `modsecurity on;` vào trong khối `location` thay vì khối `server`.
Ngoài ra, bạn có thể kết hợp ModSecurity với CrowdSec ở tầng mạng. CrowdSec phân tích log và tự động chặn IP theo hành vi, trong khi ModSecurity xử lý theo nội dung request. Hai tầng này bổ sung cho nhau. Nếu bạn quản lý VPS qua SSH công khai, hãy chắc chắn đã có fail2ban chống brute-force: bài cấu hình fail2ban bảo vệ nhiều dịch vụ trên VPS là tài liệu tham khảo tốt.
Một điểm quan trọng: OWASP CRS phát hành bản vá định kỳ để bắt kịp các kỹ thuật tấn công mới. Bạn cần có quy trình cập nhật bộ luật, ví dụ mỗi tháng chạy lại lệnh tải bản mới ở Bước 2. Không cập nhật luật là để WAF dần lỗi thời.
Cuối cùng, hiểu rằng WAF không phải là lớp bảo vệ riêng biệt. Hãy kết hợp với việc giám sát hệ thống để phát hiện bất thường. Nếu bạn quản lý nhiều VPS, nên dùng giải pháp giám sát tập trung: bài giám sát VPS với Prometheus và Grafana tự host sẽ giúp bạn có cái nhìn tổng thể.
Xử lý lỗi thường gặp
Lỗi 1: nginx -t báo "unknown directive modsecurity"
Nguyên nhân: module chưa được nạp. Kiểm tra lại dòng `load_module` trong `/etc/nginx/nginx.conf` và chắc chắn tên file module khớp với thư mục thực tế qua lệnh `find`. Sau khi sửa, chạy `sudo systemctl reload nginx`.
Lỗi 2: nginx -t báo "failed to open ... OWASP CRS"
Nguyên nhân: đường dẫn trong file `modsecurity_crs.conf` không đúng hoặc bộ luật chưa giải nén vào đúng chỗ. Dùng `ls` kiểm tra từng thư mục: `/etc/nginx/modsecurity/`, `/etc/nginx/modsecurity/rules/`. Mỗi đường dẫn trong file `Include` phải tồn tại thật.
Lỗi 3: Mọi request đều trả về 403
Nguyên nhân phổ biến nhất: thư mục `rules` chứa cả file cấu hình không phù hợp, hoặc bộ luật bị lỗi khởi tạo. Xem log `sudo journalctl -u nginx -f` để tìm dòng báo lỗi SecRule. Kiểm tra bạn đã dùng đúng bản CRS 4.x và file `crs-setup.conf` tồn tại.
Lỗi 4: Request hợp lệ bị chặn khi bật chế độ On
Đây là chuyện bình thường ở giai đoạn đầu. Chuyển `SecRuleEngine` về `DetectionOnly`, theo dõi log 2-3 ngày, gom các luật gây chặn nhầm, tạo luật loại trừ như hướng dẫn ở Bước 5, rồi mới chuyển lại `On`.
Câu hỏi thường gặp
ModSecurity có làm chậm website không?
Có, nhưng mức độ phụ thuộc cấu hình VPS và số lượng luật kích hoạt. Với VPS 2GB RAM, chi phí CPU tăng thêm khoảng 5-15% cho request thường. Nếu VPS của bạn đang dùng gần hết RAM, hãy bật WAF cho riêng location nhạy cảm và cấu hình swap trước.
OWASP CRS khác gì ModSecurity?
ModSecurity là công cụ (khung xử lý request). OWASP CRS là bộ luật (danh sách mẫu tấn công). ModSecurity không có luật thì không chặn gì, còn OWASP CRS không có ModSecurity thì không chạy được. Bạn cần cài cả hai.
ModSecurity có chặn được DDoS không?
ModSecurity không phải giải pháp chống DDoS tầng mạng. Nó chỉ lọc request độc hại về nội dung. Với tấn công DDoS làm nghẽn băng thông hoặc cạn kiệt kết nối, bạn cần giải pháp khác ở tầng hạ tầng. Nếu website bạn hay bị tấn công, nên chọn VPS có hạ tầng mạng ổn định tại Việt Nam để giảm độ trễ khi lọc request.
ModSecurity có chặn được toàn bộ tấn công không?
Không. ModSecurity chặn các mẫu tấn công đã biết và các biến thể dựa trên luật lệ. Tấn công zero-day (lỗ hổng chưa công bố) hoặc tấn công tầng logic nghiệp vụ (ví dụ nhập số lượng âm trong giỏ hàng) thì WAF không nhận diện được. Đây là lý do vì sao phải vá lỗ hổng ứng dụng kịp thời.
Dùng ModSecurity trên VPS có cần cấu hình gì thêm không?
Có. Sau khi cài xong, bạn nên cấu hình log rotate cho file `/var/log/modsec_audit.log` để tránh đầy ổ đĩa. Log này mỗi request bị chặn ghi khá nhiều dòng. Cấu hình logrotate tương tự như hướng dẫn trong bài cấu hình logrotate trên VPS quản lý log hiệu quả.
Bài viết liên quan
- Cài đặt CrowdSec chống tấn công tự động cho VPS
- 10 cách tăng cường bảo mật VPS Linux chống tấn công 2026
- Kiểm tra và vá lỗ hổng bảo mật VPS với Lynis
- Cấu hình fail2ban bảo vệ nhiều dịch vụ trên VPS


