Cú sốc lúc 2 giờ sáng: Khi HTTPS bên ngoài chỉ là lớp vỏ
Tiếng chuông điện thoại lúc 2 giờ sáng thường báo hiệu một thảm họa. Đồng nghiệp bên team Security gửi cho mình file .pcap thu thập từ một node bị chiếm quyền trong mạng nội bộ. Mở Wireshark, mình lặng người. Toàn bộ câu lệnh SQL, thông tin khách hàng và session token hiện ra rõ mồn một dưới dạng plaintext.
Sai lầm của mình là quá tin tưởng vào lớp tường thành HTTPS phía ngoài. Mình dùng Certbot cho Nginx và cấu hình chuyển hướng 80 sang 443 rất chỉn chu. Tuy nhiên, traffic giữa Web Server và Database lại chạy “trần trụi” qua port 3306. Trong mắt hacker, mạng nội bộ giống như một cái ao tù. Chỉ cần lọt vào được một node, chúng có thể thong dong “hứng” sạch mọi dữ liệu quý giá của bạn.
So sánh các kiểu mã hóa dữ liệu trên đường truyền
Để sửa sai, mình rà soát lại các cách tiếp cận phổ biến trước khi chọn phương án chốt hạ cho hệ thống.
1. SSL Termination (Chỉ mã hóa ở cửa ngõ)
Đây là mô hình nhà nhà đều dùng. TLS kết thúc tại Load Balancer hoặc Nginx. Dữ liệu đi vào mạng nội bộ là HTTP hoặc SQL thuần túy.
- Ưu điểm: Setup cực nhanh, server phía sau nhẹ gánh vì không phải giải mã.
- Nhược điểm: Data hoàn toàn lộ thiên. Nếu hacker chiếm được một container cùng subnet, mọi nỗ lực bảo mật bên ngoài coi như đổ sông đổ biển.
2. SSL Pass-through (Mã hóa xuyên suốt nhưng mù mờ)
Dữ liệu được mã hóa từ client và đi thẳng xuống server đích. Gateway lúc này không can thiệp vào quá trình giải mã.
- Ưu điểm: Bảo mật cao vì Gateway không đọc được nội dung.
- Nhược điểm: Bạn không thể dùng WAF (Web Application Firewall) hay cân bằng tải ở Layer 7 (như soi header, cookie).
3. TLS End-to-End (Mã hóa từng chặng – Lựa chọn tối ưu)
Mã hóa chặng 1 từ Client đến Nginx, sau đó tiếp tục mã hóa chặng 2 từ App đến Database. Cách này vừa bảo vệ data toàn diện, vừa giữ được sự linh hoạt để kiểm soát traffic ở Gateway.
Được và mất khi gồng mình triển khai End-to-End
Không có giải pháp nào là miễn phí hoàn toàn. Việc ép mọi kết nối phải chạy TLS sẽ mang lại những đánh đổi mà bạn cần lưu ý:
- An tâm tuyệt đối: Ngay cả khi sniffing được traffic trên switch, hacker chỉ thấy những khối ký tự vô nghĩa.
- Vượt qua các bài Audit: Với dự án tài chính hoặc y tế (PCI-DSS, HIPAA), mã hóa data in-transit là điều kiện “sống còn”.
- Áp lực hiệu năng (Overhead): Quá trình bắt tay (Handshake) làm tăng latency thêm khoảng 2-5ms mỗi request. Tuy nhiên, các CPU hiện đại hỗ trợ AES-NI đã xử lý việc này rất mượt mà, hiếm khi gây nghẽn cổ chai.
Thực chiến: Khóa hòm kết nối Nginx -> App -> MySQL
Dưới đây là quy trình mình đã thực hiện để bít kín lỗ hổng ngay trong đêm đó.
Bước 1: Tự dựng CA nội bộ
Vì Database nằm trong mạng riêng, mình không dùng Let’s Encrypt mà tự tạo một Certificate Authority (CA) riêng để ký cert.
# Tạo Root CA dùng trong 10 năm
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem
# Tạo Key và Cert cho MySQL Server
openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server-req.pem
openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
Bước 2: Cấu hình MySQL Server
Đưa các file cert vào thư mục /etc/mysql/ssl/ và phân quyền chown mysql:mysql. Tiếp theo, hãy cập nhật file my.cnf:
[mysqld]
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
# Chặn đứng mọi kết nối không dùng SSL
require_secure_transport = ON
Sau đó, khởi động lại dịch vụ: systemctl restart mysql.
Bước 3: Kết nối từ Application
Lúc này, Web App cần file ca-cert.pem để xác thực danh tính Database. Khi tạo user mới, mình dùng Password Generator để lấy một chuỗi 32 ký tự ngẫu nhiên cho an toàn.
Ví dụ cách cấu hình với SQLAlchemy trong Python:
ssl_params = {
'ca': '/path/to/ca-cert.pem',
'check_hostname': True
}
engine = create_engine(
"mysql+pymysql://user:[email protected]/prod_db",
connect_args={"ssl": ssl_params}
)
Kiểm chứng thành quả
Mọi cấu hình đều vô nghĩa nếu không chạy thực tế. Mình dùng client MySQL từ Web Server để kiểm tra:
mysql -u user -p -h 10.0.0.5 --ssl-ca=ca-cert.pem
mysql> \s
--------------
SSL: Cipher in use is TLS_AES_256_GCM_SHA384
--------------
Dòng chữ Cipher in use... xuất hiện đồng nghĩa với việc dữ liệu của bạn đã được đóng gói an toàn trong lớp kén TLS.
Bài học sau một đêm thức trắng
Triển khai TLS nội bộ khiến việc debug bằng tcpdump khó khăn hơn. Thế nhưng, đổi lại là giấc ngủ ngon mà không phải lo sợ những đợt audit hay hacker rình rập. Đừng đợi đến lúc dữ liệu khách hàng bị rao bán trên forum mới cuống cuồng đi cài SSL cho Database.
Bảo mật không phải là đích đến cuối cùng. Đó là nỗ lực thắt chặt từng kẽ hở nhỏ nhất mỗi ngày.

