MySQL SSL/TLS: Đừng để dữ liệu “nude” trên đường truyền (Hướng dẫn cấu hình mTLS)

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

MySQL mà không có SSL? Bạn đang chơi đùa với lửa!

Mặc định, MySQL truyền dữ liệu dưới dạng plain text. Nếu ứng dụng và database nằm cùng một server qua socket thì không sao. Tuy nhiên, khi bạn chạy microservices hoặc kết nối qua Internet giữa các vùng Cloud, đây là một lỗ hổng cực lớn. Chỉ cần 5 phút với công cụ như Wireshark, một hacker có thể dễ dàng bắt được toàn bộ câu lệnh SQL, từ mật khẩu admin đến thông tin thẻ tín dụng khách hàng.

Trước đây, mình từng xử lý một sự cố khi hệ thống scale lên 50 node. Do chủ quan không bật mã hóa trên mạng nội bộ, đợt audit bảo mật định kỳ đã phát hiện toàn bộ payload thanh toán bị lộ thiên hoàn toàn. Đó là bài học xương máu khiến mình luôn đặt việc triển khai SSL/TLS lên ưu tiên hàng đầu ngay từ bước khởi tạo database.

Ba cấp độ bảo mật kết nối bạn cần biết

Tùy vào yêu cầu dự án, bạn có thể chọn một trong ba mức độ sau:

  • Kết nối trần (Unencrypted): Tốc độ nhanh nhất nhưng rủi ro cao nhất. Chỉ nên dùng khi kết nối nội bộ qua localhost.
  • SSL tiêu chuẩn (One-way TLS): Server chìa certificate ra để client xác thực. Dữ liệu được mã hóa, chống nghe lén, nhưng server không thực sự biết client là ai.
  • Xác thực Client Certificate (Two-way TLS/mTLS): Cấp độ “hardcore” nhất. Cả server và client đều phải xuất trình cert hợp lệ do cùng một CA ký. Hacker dù có password mà thiếu file private key trên máy thì cũng chỉ biết đứng ngoài nhìn.

So sánh nhanh các phương thức:

Tiêu chí Không mã hóa Standard SSL Client Cert (mTLS)
Mã hóa dữ liệu Không
Xác thực Server Không
Xác thực Client Chỉ Password Chỉ Password Cert + Password
Độ an toàn Thấp Trung bình Tối đa

Tại sao mTLS là lựa chọn của các hệ thống lớn?

Trong thực tế, mật khẩu rất dễ bị rò rỉ qua file cấu hình hoặc biến môi trường. Khi áp dụng Client Certificate, bạn đã thêm một lớp bảo mật vật lý. Ngay cả khi chiếm được thông tin đăng nhập, kẻ tấn công vẫn cần file key được lưu trữ bảo mật trên server ứng dụng. Đây là cách hiệu quả nhất để cô lập database, chỉ cho phép những node cụ thể có quyền truy cập.

Từng bước triển khai thực tế

Bước 1: Tự dựng Certificate Authority (CA) nội bộ

Với kết nối nội bộ, mình khuyên bạn nên tự tạo CA riêng để chủ động quản lý thay vì dùng Let’s Encrypt. Hãy dùng OpenSSL để tạo bộ key này:

# Tạo CA (Key và Certificate tự ký)
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca.pem -subj "/CN=MySQL_Internal_CA"

# Tạo Key và Cert cho Server
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem -subj "/CN=mysql-server"
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

# Tạo Key và Cert cho Client
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem -out client-req.pem -subj "/CN=mysql-client-app-1"
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 02 -out client-cert.pem

Kết quả bạn có bộ 5 file. Hãy giữ ca-key.pem thật kỹ ở một nơi an toàn (offline càng tốt).

Bước 2: Cấu hình phía MySQL Server

Chép các file ca.pem, server-cert.pem, server-key.pem vào thư mục /etc/mysql/ssl/. Đừng quên phân quyền chown mysql:mysql cho chúng. Sau đó, cập nhật file cấu hình my.cnf:

[mysqld]
ssl-ca=/etc/mysql/ssl/ca.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem

# Chặn hoàn toàn các kết nối không mã hóa
require_secure_transport = ON

Khởi động lại service để áp dụng thay đổi: sudo systemctl restart mysql.

Bước 3: Tạo User yêu cầu xác thực X509

Đây là bước then chốt. Chúng ta sẽ tạo một user mà MySQL bắt buộc phải kiểm tra cert mới cho phép vào cửa:

CREATE USER 'app_user'@'%' IDENTIFIED BY 'mat_khau_sieu_kho_123';
GRANT ALL PRIVILEGES ON prod_db.* TO 'app_user'@'%' REQUIRE X509;
FLUSH PRIVILEGES;

Từ khóa REQUIRE X509 đảm bảo rằng dù hacker có biết mật khẩu cũng không thể login nếu không có cert do bạn cấp.

Bước 4: Kết nối từ Client

Thử nghiệm nhanh qua CLI để kiểm tra cấu hình:

mysql -u app_user -p -h db.example.com \
    --ssl-ca=ca.pem \
    --ssl-cert=client-cert.pem \
    --ssl-key=client-key.pem

Ví dụ kết nối bằng Python:

import mysql.connector

conn = mysql.connector.connect(
    user='app_user',
    password='mat_khau_sieu_kho_123',
    host='10.0.0.5',
    database='prod_db',
    ssl_ca='certs/ca.pem',
    ssl_cert='certs/client-cert.pem',
    ssl_key='certs/client-key.pem'
)

cursor = conn.cursor()
cursor.execute("SHOW STATUS LIKE 'Ssl_cipher'")
print(f"Đang kết nối bằng thuật toán: {cursor.fetchone()[1]}")
conn.close()

Vài lưu ý “xương máu” về vận hành

Sau vài năm duy trì hệ thống chạy SSL, mình rút ra 3 điểm quan trọng:

  1. Hiệu năng (CPU Overhead): Handshake SSL tiêu tốn tài nguyên đáng kể. Trong một test benchmark, throughput có thể giảm từ 1000 req/s xuống 600 req/s nếu tạo kết nối mới liên tục. Hãy dùng Connection Pool (như ProxySQL) để duy trì kết nối SSL luôn mở.
  2. Đừng để Cert hết hạn: Cảm giác tệ nhất là hệ thống sập lúc 2h sáng chỉ vì cert hết hạn. Hãy đặt monitor hoặc dùng script tự động cảnh báo trước 30 ngày.
  3. Lỗi chuỗi CA: Nếu gặp lỗi self signed certificate in certificate chain, 90% là do file ca.pem trên máy client không khớp với CA đã dùng để ký cert trên server. Hãy kiểm tra lại checksum của chúng.

Triển khai SSL/TLS có thể hơi rườm rà lúc đầu. Tuy nhiên, nó giúp bạn ngủ ngon hơn khi biết rằng dữ liệu của mình không còn “lộ thiên” trước ánh mắt tò mò của hacker.

Share: