Chặn đứng SQL Injection cho MySQL Production bằng ProxySQL Firewall

Database tutorial - IT technology blog
Database tutorial - IT technology blog

Tại sao bảo mật tầng ứng dụng là chưa đủ?

Dù team dev có cẩn thận đến đâu, lỗ hổng SQL Injection (SQLi) vẫn luôn là bóng ma ám ảnh các hệ thống lớn. Lỗi này thường lẻn vào qua những đoạn code cũ (legacy code) chưa kịp refactor hoặc từ các thư viện bên thứ ba mà chúng ta vô tình tin tưởng.

Chỉ dựa vào validate dữ liệu ở tầng App là bạn đang đánh cược toàn bộ database vào tay lập trình viên. Chỉ cần một phút sơ sẩy quên dùng Prepared Statements, kẻ tấn công có thể dump toàn bộ dữ liệu chỉ bằng một câu query tìm kiếm đơn giản. Mình từng xử lý một sự cố rò rỉ 50.000 bản ghi khách hàng chỉ vì một tham số lọc (filter) bị bỏ sót. Đó là lý do bạn cần một chốt chặn thứ hai nằm ngay trước Database: ProxySQL Firewall.

So sánh các phương pháp bảo vệ Database

Trước khi cấu hình ProxySQL, hãy nhìn lại các giải pháp hiện nay để thấy tại sao nó lại đặc biệt:

  • WAF (Web Application Firewall): Cloudflare hay ModSecurity chặn request HTTP rất tốt. Tuy nhiên, chúng không hiểu sâu giao thức MySQL và dễ bị qua mặt bởi các kỹ thuật xáo trộn (obfuscation) SQL phức tạp.
  • Hardening tại Database: Việc giới hạn quyền user (GRANT/REVOKE) là bắt buộc. Dù vậy, nó không ngăn được một user hợp lệ bị chiếm quyền để thực hiện lệnh xóa dữ liệu hàng loạt.
  • ProxySQL Firewall: Hoạt động như một Layer 7 proxy. Nó soi xét từng câu lệnh SQL, đối chiếu với pattern và quyết định cho qua, chặn lại hoặc sửa đổi (rewrite) ngay lập tức.

Phối hợp cả ba lớp là phương án tối ưu, nhưng ProxySQL chính là lớp phòng thủ cuối cùng đáng tin cậy nhất.

Cơ chế hoạt động của ProxySQL Firewall

ProxySQL không chặn theo IP như firewall truyền thống mà chặn dựa trên nội dung câu lệnh. Nó sử dụng bảng mysql_query_rules để định nghĩa các quy tắc kiểm soát.

Khi một query đi qua, ProxySQL so khớp nó với match_pattern (thường là Regex). Nếu khớp, hệ thống sẽ thực hiện một trong các hành động:

  • OK: Cho phép thực thi.
  • BLOCK: Trả về lỗi ngay cho App, không gửi query tới DB để bảo vệ tài nguyên.
  • REWRITE: Tự động sửa lại câu query cho an toàn trước khi gửi đi.

Triển khai thực tế: Chặn đứng truy vấn nguy hiểm

Giả sử bạn đã có cụm ProxySQL làm load balancer. Hãy biến nó thành một Firewall thực thụ qua hai bước sau.

Bước 1: Thiết lập danh sách đen (Blacklisting)

Đây là cách nhanh nhất để chặn các pattern kinh điển như OR 1=1 hoặc các lệnh phá hoại như DROP TABLE từ phía web.

-- Truy cập admin interface
mysql -u admin -padmin -h 127.0.0.1 -P6032

-- Chặn truy vấn chứa 'OR 1=1' (thường dùng để bypass login)
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, error_msg, apply) 
VALUES (100, 1, '(?i)OR\s+\d+=\d+', 'Access Denied: Potential SQL Injection Detected', 1);

-- Chặn lệnh DROP TABLE từ user ứng dụng
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, error_msg, apply) 
VALUES (101, 1, '(?i)^DROP\s+TABLE', 'Access Denied: Dangerous command not allowed', 1);

-- Áp dụng thay đổi ngay lập tức
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

Flag (?i) giúp Regex không phân biệt chữ hoa chữ thường. Nếu kẻ tấn công dùng công cụ như sqlmap để dò tìm, ProxySQL sẽ lập tức chặn đứng và trả về thông báo lỗi tùy chỉnh.

Bước 2: Whitelisting (Chiến lược an toàn tuyệt đối)

Blacklisting không bao giờ là đủ vì hacker luôn tìm ra cách lách luật. Phương pháp Whitelisting chỉ cho phép các câu query đã được phê duyệt đi qua, mọi thứ khác đều bị cấm.

Đầu tiên, hãy bật chế độ log để thu thập các query “sạch” đang chạy:

-- Ghi log các query để phân tích
UPDATE mysql_query_rules SET log=1 WHERE rule_id=...; 

Sau khi xác định danh sách query an toàn, bạn tạo rule cho phép chúng với apply=1. Cuối cùng, thêm một rule có rule_id lớn nhất để chặn toàn bộ phần còn lại.

-- Rule cuối cùng: Chặn mọi truy vấn lạ
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, error_msg, apply) 
VALUES (9999, 1, '.', 'Access Denied: Query not whitelisted', 1);

LOAD MYSQL QUERY RULES TO RUNTIME;

Bạn cần hiểu rất rõ các truy vấn mà ứng dụng tạo ra để tránh chặn nhầm. Nếu cần xử lý log từ CSV sang JSON để phân tích whitelist nhanh hơn, mình thường dùng toolcraft.app/vi/tools/data/csv-to-json. Công cụ này chạy client-side nên dữ liệu nhạy cảm không bị đẩy lên server.

Kinh nghiệm thực chiến khi vận hành

Triển khai lớp bảo vệ này trên Production cần sự cẩn trọng để tránh gây gián đoạn dịch vụ:

  1. Chế độ quan sát: Khi tạo rule mới, hãy để active=0 và dùng log=1 để theo dõi trước. Nếu sau 24-48h không có lỗi lầm gì mới chuyển sang active=1.
  2. Ưu tiên thứ tự: ProxySQL duyệt rule từ ID thấp đến cao. Hãy đặt các rule cụ thể (Whitelist) ở ID thấp và rule chặn chung (Catch-all) ở ID cao nhất.
  3. Đo lường hiệu năng: ProxySQL xử lý Regex cực nhanh, thường chỉ thêm < 0.5ms latency. Tuy nhiên, nếu có hàng nghìn rule phức tạp, bạn cần tối ưu Regex để tránh làm chậm hệ thống.
  4. Giám sát liên tục: Theo dõi bảng stats_mysql_query_rules để biết rule nào đang bị kích hoạt bất thường, từ đó nhận diện sớm các cuộc tấn công.
-- Xem thống kê rule nào đang chặn nhiều nhất
SELECT rule_id, hits FROM stats.stats_mysql_query_rules;

Lời kết

Thiết lập ProxySQL Firewall không khó, nhưng duy trì nó yêu cầu sự tỉ mỉ theo sát mã nguồn ứng dụng. Đây là khoản đầu tư xứng đáng để bảo vệ database khỏi những sai lầm ngớ ngẩn hoặc các cuộc tấn công có chủ đích. Đừng đợi đến khi thấy lệnh DROP DATABASE xuất hiện trong log mới bắt đầu lo bảo mật.

Share: