Triển khai ModSecurity và OWASP CRS trên Nginx: Thiết lập WAF mã nguồn mở

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Đồng hồ điểm 2:14 sáng. Chuông báo động PagerDuty réo vang, và Grafana hiển thị CPU của database backend đang chạm mức 100%. Liếc nhanh qua lệnh tail -f /var/log/nginx/access.log, bạn nhận ra có tới 3.200 request POST mỗi giây đang dội liên tục vào một endpoint tìm kiếm chưa được đánh index.

Kẻ tấn công đang tung ra các mẫu dò quét SQL injection kinh điển (' OR 1=1--) cùng các đợt quét path traversal để tìm /etc/passwd. Nếu không có một lớp WAF bảo vệ, lượng traffic rác này sẽ lao thẳng vào các application worker. Mỗi truy vấn độc hại sẽ giữ chặt một worker thread, nhanh chóng làm cạn kiệt connection pool 100 kết nối tới database của bạn. Bạn cần một Web Application Firewall hoạt động inline trực tiếp ngay trong Nginx trước khi trời sáng.

Cloud Edge WAF so với In-Host ModSecurity trên Nginx

Khi các đợt tấn công dồn dập nhắm vào server gốc (origin server), bạn có hai phương án chính để chặn đứng các gói tin độc hại: tường lửa đám mây ở upstream hoặc một engine nội bộ trên máy chủ (in-host) như ModSecurity.

Cả hai mô hình đều đối chiếu payload với các signature lỗ hổng đã biết, nhưng chúng hoạt động ở các tầng khác nhau trong kiến trúc của bạn:

  • Cloud WAF (AWS WAF, Cloudflare, Fastly): Nằm ở rìa DNS và CDN (edge). Tường lửa này kiểm tra các request trước khi traffic kịp chạm tới VPS hay giao diện mạng bare-metal của bạn. Bạn quản lý các policy qua console của nhà cung cấp hoặc script Terraform.
  • In-Host ModSecurity v3 (libmodsecurity + Nginx Connector): Chạy bên trong instance Nginx của bạn dưới dạng dynamic module. Nó kiểm tra header, tham số, cookie và request body ngay trước khi chuyển tiếp chúng tới các socket ứng dụng nội bộ.

Dưới đây là so sánh giữa hai cách tiếp cận trong hoạt động vận hành hàng ngày:

Chỉ số vận hành Cloud Edge WAF In-Host ModSecurity + OWASP CRS
Thiết lập triển khai Chuyển đổi DNS nhanh chóng, nhưng yêu cầu định tuyến bên ngoài và chia sẻ chứng chỉ SSL. Yêu cầu biên dịch hoặc nạp các dynamic module vào Nginx.
Quyền riêng tư dữ liệu & Tuân thủ Payload TLS đã giải mã sẽ đi qua nhà cung cấp CDN bên thứ ba. Không để lộ dữ liệu cho bên thứ ba. Toàn bộ payload chỉ nằm trong RAM của máy chủ.
Tấn công lưu lượng lớn (Volumetric Floods) Hấp thụ các đợt tấn công nhiều gigabit trước khi giao diện mạng vật lý bị nghẽn. Không thể bảo vệ bạn nếu cuộc tấn công làm bão hòa băng thông uplink của máy chủ.
Quyền kiểm soát Rule Engine Phụ thuộc vào hạn mức rule của nhà cung cấp và các gói tính năng thương mại. Toàn quyền kiểm soát các chỉ thị SecRule, regex PCRE, Lua và các file rule CRS gốc.
Khả năng dự đoán chi phí Tính phí theo mỗi triệu request cộng với băng thông được kiểm tra. Đột biến lưu lượng lớn sẽ rất tốn kém. Phần mềm mã nguồn mở miễn phí, chạy hoàn toàn trên các lõi CPU hiện có của bạn.

Cân nhắc giữa kiểm tra in-engine và lọc ở biên (edge)

Chạy tường lửa ngay bên trong Nginx mang lại những lợi thế rõ rệt, nhưng nó cũng đi kèm với các trách nhiệm vận hành thực tế.

Ưu điểm của ModSecurity trên Nginx

  • Chủ quyền dữ liệu (Data Sovereignty): Không có bất kỳ proxy bên thứ ba nào đọc được payload của khách hàng, session cookie hay token xác thực. Với các yêu cầu cô lập bắt buộc như HIPAA, PCI-DSS hoặc GDPR, kiến trúc này giúp loại bỏ rào cản về tuân thủ.
  • Ngữ cảnh nội bộ: ModSecurity kiểm tra được các header định tuyến nội bộ, các rewrite đường dẫn của microservice và các upstream proxy header mà CDN bên ngoài không bao giờ thấy được.
  • Chi phí cố định, rõ ràng: Lưu lượng tăng đột biến sẽ không tạo ra các hóa đơn kiểm tra bảo mật bất ngờ lên đến năm con số vào cuối tháng.

Nhược điểm của ModSecurity trên Nginx

  • Độ trễ CPU: Việc phân tích biểu thức chính quy (regex) trên request body đòi hỏi sức mạnh tính toán. Chạy các bộ rule phức tạp thường tăng thêm 2ms đến 8ms độ trễ xử lý cho mỗi request HTTP trong điều kiện tải tiêu chuẩn.
  • Cảnh báo giả (False Positives): Các rule mặc định có thể chặn nhầm payload JSON hợp lệ hoặc nội dung Markdown do người dùng gửi lên. Bạn sẽ cần viết các rule loại trừ (exclusion rules) cho traffic hợp lệ.
  • Ràng buộc binary (Binary Coupling): Connector module được liên kết chặt chẽ với đúng chuẩn ABI của bản Nginx bạn đang chạy. Các bản nâng cấp package nhỏ của Nginx có thể yêu cầu biên dịch lại ngx_http_modsecurity_module.so để tránh xung đột binary.

Kiến trúc khuyến nghị: Libmodsecurity3 và OWASP CRS v4

Tránh viết các rule tùy biến chắp vá trong khi sự cố đang diễn ra. Nền tảng đã qua thực chiến dày dạn để ngăn chặn các cuộc tấn công zero-day và các lỗ hổng OWASP Top 10 là ModSecurity v3 kết hợp cùng OWASP Core Rule Set (CRS) v4.

Đối với máy chủ Ubuntu 22.04 và 24.04 LTS, hãy xây dựng hệ thống xoay quanh ba trụ cột sau:

  • Core Engine: libmodsecurity3 được biên dịch dưới dạng thư viện C++ chia sẻ (shared library).
  • Nginx Connector: ngx_http_modsecurity_module.so được nạp động (dynamically) vào package Nginx hiện tại của bạn.
  • Rule phát hiện: OWASP CRS v4 chạy ở chế độ Anomaly Scoring Mode với Paranoia Level 1.

Hướng dẫn triển khai từng bước

Làm theo các bước sau để biên dịch engine, nạp connector vào Nginx và kích hoạt thực thi các rule bảo vệ.

Bước 1: Cài đặt các dependency để biên dịch

Cài đặt trình biên dịch, công cụ build và các header phát triển cần thiết cho PCRE2, phân tích cú pháp XML và xử lý JSON:

sudo apt-get update
sudo apt-get install -y apt-utils autoconf automake build-essential \
    git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre2-dev \
    libpcre3-dev libtool libxml2-dev libyajl-dev pkgconf zlib1g-dev

Bước 2: Biên dịch và cài đặt Libmodsecurity v3

Tải nhánh v3 độc lập và biên dịch vào các đường dẫn thư viện hệ thống tiêu chuẩn:

cd /usr/local/src
sudo git clone --depth 1 -b v3/master https://github.com/owasp-modsecurity/ModSecurity.git
cd ModSecurity
sudo git submodule init
sudo git submodule update
sudo ./build.sh
sudo ./configure
sudo make -j$(nproc)
sudo make install
sudo ldconfig

Thao tác này sẽ biên dịch và đặt file libmodsecurity.so vào thư mục /usr/local/modsecurity/lib/.

Bước 3: Biên dịch dynamic connector cho Nginx

Connector module phải khớp chính xác từng byte với binary Nginx đang chạy. Hãy kiểm tra phiên bản package hiện tại của bạn:

nginx -v
# Kết quả: nginx version: nginx/1.24.0

Tải bản phát hành mã nguồn tương ứng, cấu hình dynamic module với cờ --with-compat và tiến hành biên dịch:

cd /usr/local/src
sudo git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git

wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzvf nginx-1.24.0.tar.gz
cd nginx-1.24.0

./configure --with-compat --add-dynamic-module=/usr/local/src/ModSecurity-nginx
make modules

sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/

Bước 4: Cấu hình ModSecurity và OWASP CRS v4

Tạo một cây thư mục gọn gàng bên trong /etc/nginx/modsec/ để chứa các file cấu hình và các gói rule:

sudo mkdir -p /etc/nginx/modsec
cd /usr/local/src/ModSecurity
sudo cp modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf
sudo cp unicode.mapping /etc/nginx/modsec/

cd /etc/nginx/modsec
sudo git clone --depth 1 -b v4.0/master https://github.com/coreruleset/coreruleset.git
sudo cp coreruleset/crs-setup.conf.example coreruleset/crs-setup.conf

Tạo file điểm vào chính /etc/nginx/modsec/main.conf để nạp các thiết lập cốt lõi, cấu hình khởi tạo và các rule CRS:

cat << 'EOF' | sudo tee /etc/nginx/modsec/main.conf
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/coreruleset/crs-setup.conf
Include /etc/nginx/modsec/coreruleset/rules/*.conf
EOF

Bước 5: Tinh chỉnh chế độ Engine và Audit Logging

Tránh làm gián đoạn các phiên người dùng hợp lệ do chặn quá nghiêm ngặt trong quá trình chuyển đổi trên production. Mở file /etc/nginx/modsec/modsecurity.conf và kiểm tra chỉ thị engine của bạn:

sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsec/modsecurity.conf

Nếu bạn có các API lưu lượng cao với cấu trúc payload đặc thù, hãy duy trì thiết lập DetectionOnly trong 30 đến 60 phút. Xem xét các lượt kích hoạt được ghi log trước, tinh chỉnh để loại bỏ các cảnh báo giả (false positives), sau đó mới chuyển chỉ thị sang On.

Trong khi thắt chặt quyền truy cập quản trị vào các giao diện quản lý và luân chuyển thông tin đăng nhập trong quá trình bảo trì máy chủ, tôi sử dụng công cụ tạo mật khẩu tại toolcraft.app/vi/tools/security/password-generator cho mật khẩu máy chủ — công cụ này chạy hoàn toàn trên trình duyệt nên không có dữ liệu nào bị gửi qua mạng. Khi các bề mặt quản trị trên production đã được bảo vệ bằng các chuỗi bí mật có độ hỗn loạn (entropy) cao, hãy chuyển sự chú ý sang việc tích hợp vào Nginx.

Bước 6: Nạp module và gắn vào Virtual Host

Thêm dòng nạp module vào ngay dòng đầu tiên của file /etc/nginx/nginx.conf:

load_module modules/ngx_http_modsecurity_module.so;

user www-data;
worker_processes auto;
pid /run/nginx.pid;

Bật ModSecurity bên trong khối server của virtual host mục tiêu:

server {
    listen 80;
    server_name api.example.com;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Kiểm tra cấu hình, sau đó reload lại các worker process mà không gây gián đoạn dịch vụ (zero downtime):

sudo nginx -t && sudo systemctl reload nginx

Bước 7: Xác minh tính năng chặn và Audit Log

Gửi một request mô phỏng dò quét path traversal từ client bên ngoài để kiểm tra xem Nginx có chặn request hay không:

curl -i "http://api.example.com/?exec=/etc/passwd"

Reverse proxy của bạn sẽ từ chối payload ngay lập tức với mã trạng thái 403 Forbidden:

HTTP/1.1 403 Forbidden
Server: nginx
Date: Thu, 15 Oct 2026 02:35:10 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive

Kiểm tra bản ghi log tương ứng trong /var/log/nginx/error.log:

sudo tail -n 20 /var/log/nginx/error.log

Bạn sẽ thấy rule 930110 hoặc 930120 được kích hoạt: [id "930110"] [msg "Tấn công Path Traversal (/../)"] [data "Dữ liệu khớp: tìm thấy /etc/passwd"] [severity "CRITICAL"]. Gói tin bị loại bỏ trước khi kịp tới các worker ở upstream, connection pool của database được giải phóng, và độ trễ của ứng dụng hồi phục trở lại.

Share: