Bảo mật cơ sở dữ liệu PostgreSQL trên Production: Mã hóa SSL, pg_hba.conf và pgAudit

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

Vấn đề thực tế: Bẫy cấu hình mặc định của cơ sở dữ liệu

Nhiều năm trước, tôi triển khai cụm PostgreSQL production đầu tiên cho một dịch vụ fintech có lưu lượng truy cập cao, xử lý khoảng 12.000 request mỗi phút. Tôi làm theo một hướng dẫn cài đặt nhanh, mở port 5432 và kết nối các microservice backend vào.

Mọi thứ vận hành trơn tru cho đến khi đội ngũ bảo mật tiến hành đợt penetration test đầu tiên. Kết quả kiểm tra thật sự đáng báo động: thông tin đăng nhập database được truyền dưới dạng plaintext trong mạng subnet nội bộ, bảng cấu hình xác thực cho phép kết nối từ dải IP quá rộng, và chúng tôi hoàn toàn không có nhật ký ghi lại ai đã chỉnh sửa các bản ghi dữ liệu quan trọng.

Các đội ngũ kỹ sư rất hay rơi vào cái bẫy này. Mọi người thường khởi tạo một managed instance hoặc máy ảo rồi chủ quan nghĩ rằng chỉ riêng ranh giới VPC đã đủ để ngăn chặn kẻ tấn công. Thực tế không phải vậy. Chỉ cần một container trong cụm bị xâm nhập, kẻ tấn công đã có bàn đạp để bắt gói tin database, đánh cắp mật khẩu và thực hiện drop bảng mà không hề bị phát hiện.

Phân tích nguyên nhân gốc rễ: Vì sao cấu hình mặc định của PostgreSQL là chưa đủ

PostgreSQL đi kèm với các thiết lập mặc định được tối ưu hóa cho môi trường phát triển local mượt mà nhất. Dù điều này giúp bạn khởi chạy chỉ trong vài phút, nhưng nó lại để lại những lỗ hổng lớn trong môi trường production:

  • Truyền tải không mã hóa theo mặc định: Các bản build PostgreSQL hiện đại đều hỗ trợ sẵn SSL. Dù vậy, cấu hình mặc định thường để ssl = off hoặc cho phép client âm thầm fallback về kết nối TCP không mã hóa mà không có bất kỳ cảnh báo nào.
  • Quy tắc xác thực quá lỏng lẻo: Các cấu hình mặc định cũ trong pg_hba.conf thường dựa vào thuật toán băm yếu như MD5, thậm chí là phương thức xác thực trust trên interface nội bộ. Những cấu hình này khiến hash mật khẩu dễ bị tấn công vét cạn (offline dictionary attack).
  • Điểm mù trong hệ thống log tiêu chuẩn: PostgreSQL chỉ ghi log lỗi cú pháp, ngắt kết nối và các truy vấn chậm vượt ngưỡng log_min_duration_statement. Tuy nhiên, nó bỏ qua hoàn toàn việc theo dõi chi tiết các thao tác DDL và DML. Điều này đồng nghĩa với việc bạn không thể chứng minh sự tuân thủ các tiêu chuẩn như HIPAA, SOC 2 hay PCI-DSS.

So sánh các chiến lược phòng thủ

Bảo vệ cụm database production đòi hỏi phải lựa chọn kiến trúc vừa bảo vệ an toàn dữ liệu, vừa không làm giảm throughput truy vấn hay gây phức tạp cho quy trình deployment.

Chiến lược bảo mật Ưu điểm Nhược điểm Mức độ phù hợp
Chỉ dùng tường lửa mạng (VPC / Security Group) Không cần thay đổi cấu hình; thiết lập ban đầu nhanh chóng. Hoàn toàn không có khả năng phòng thủ khi một instance nội bộ bị xâm nhập; không mã hóa gói tin; không có audit trail. Chỉ dùng cho môi trường development và sandbox.
Log mặc định của Engine + Mật khẩu Overhead thấp (~1% tác động CPU); ghi log các lần đăng nhập thất bại và slow query. Không có audit trail chi tiết ở cấp độ bảng; mật khẩu vẫn có nguy cơ bị lộ khi truyền tải nếu không có SSL. Môi trường staging không chứa dữ liệu quan trọng.
Bảo mật đa lớp (TLS 1.3 + SCRAM-SHA-256 + pgAudit) Mã hóa toàn bộ dữ liệu truyền tải, hàm băm mã hóa mạnh và log tuân thủ chống can thiệp. Yêu cầu tự động hóa gia hạn chứng chỉ và tốn thêm khoảng 3–7% dung lượng lưu trữ log. Bắt buộc đối với tất cả cơ sở dữ liệu production.

Giải pháp tối ưu: Hướng dẫn hardening từng bước

Thực hiện theo các bước sau để tăng cường bảo mật cho bản cài đặt PostgreSQL 16 tiêu chuẩn, sẵn sàng cho môi trường production quan trọng.

Bước 1: Bắt buộc mã hóa truyền tải với TLS 1.3

Bắt đầu bằng cách tạo hoặc cấp phát chứng chỉ TLS để mã hóa toàn bộ lưu lượng giữa các application server và database engine. Đối với hạ tầng nội bộ, hãy sử dụng chứng chỉ do Certificate Authority (CA) nội bộ của tổ chức cấp (như HashiCorp Vault hoặc cert-manager) hoặc tạo một cặp khóa 4096-bit chuyên dụng.

# Di chuyển đến thư mục cấu hình của PostgreSQL
cd /etc/postgresql/16/main

# Tạo private key RSA 4096-bit
openssl genrsa -out server.key 4096

# Giới hạn quyền truy cập file (PostgreSQL sẽ từ chối key nếu phân quyền quá lỏng lẻo)
chmod 600 server.key
chown postgres:postgres server.key

# Tạo chứng chỉ self-signed có hiệu lực trong 365 ngày
openssl req -new -x509 -days 365 -key server.key -out server.crt \
  -subj "/CN=db-primary.internal.infra"

Tiếp theo, chỉnh sửa file postgresql.conf để bật TLS, chỉ định các phiên bản giao thức hiện đại và liên kết các file chứng chỉ:

# Trong file /etc/postgresql/16/main/postgresql.conf
ssl = on
ssl_cert_file = '/etc/postgresql/16/main/server.crt'
ssl_key_file = '/etc/postgresql/16/main/server.key'
ssl_min_protocol_version = 'TLSv1.3'
ssl_ciphers = 'HIGH:!aNULL:!MD5:!3DES'

Bước 2: Siết chặt quyền truy cập client với pg_hba.conf và SCRAM

File pg_hba.conf đóng vai trò như tường lửa cho cơ sở dữ liệu của bạn. Chúng ta sẽ vô hiệu hóa phương thức xác thực MD5 lỗi thời, bắt buộc dùng scram-sha-256 và ép các client từ xa phải kết nối qua chỉ thị hostssl.

Trước khi khởi tạo user cho ứng dụng, hãy tạo mật khẩu mạnh và an toàn về mặt mật mã học. Một công cụ tạo mật khẩu chạy trên trình duyệt như tại toolcraft.app/vi/tools/security/password-generator rất phù hợp để tạo chuỗi 32 ký tự có độ phức tạp cao mà không làm rò rỉ secret qua mạng. Sau khi tạo xong, hãy khởi tạo role cho ứng dụng:

-- Đảm bảo mật khẩu sử dụng cơ chế băm SCRAM trước khi tạo role
SET password_encryption = 'scram-sha-256';

-- Tạo role ứng dụng với nguyên tắc đặc quyền tối thiểu (least privilege)
CREATE ROLE api_gateway WITH LOGIN PASSWORD 'xK9#mQ2$vL8*pZ4!wR7@yN1&jB6^tE5(';
GRANT CONNECT ON DATABASE orders_prod TO api_gateway;

Bây giờ, hãy mở file pg_hba.conf và thay thế các quy tắc mặc định bằng các dải CIDR cụ thể, thu hẹp:

# TYPE     DATABASE        USER            ADDRESS                 METHOD

# Bảo trì admin nội bộ qua Unix socket
local    all             postgres                                peer

# Từ chối toàn bộ lưu lượng TCP không mã hóa, chỉ yêu cầu SCRAM từ subnet của API
hostssl  orders_prod     api_gateway     10.0.4.0/24             scram-sha-256

# Từ chối dứt khoát tất cả các kết nối còn lại
host     all             all             0.0.0.0/0               reject
host     all             all             ::0/0                   reject

Bước 3: Theo dõi các thay đổi trong cơ sở dữ liệu với pgAudit

Log tiêu chuẩn cho biết khi nào một truy vấn bị lỗi, nhưng chúng sẽ không giúp ích được gì khi điều tra sự cố rò rỉ dữ liệu. Extension pgAudit tạo ra nhật ký kiểm toán có cấu trúc, ghi lại chi tiết mọi lệnh INSERT, UPDATE, DELETE và ALTER trên các bảng nhạy cảm.

Cài đặt gói extension tương ứng với phiên bản PostgreSQL của bạn:

# Ubuntu / Debian
sudo apt-get update && sudo apt-get install -y postgresql-16-pgaudit

Cấu hình PostgreSQL để tải trước shared library và ghi log các thay đổi quản trị cũng như thao tác ghi dữ liệu:

# Trong file /etc/postgresql/16/main/postgresql.conf
shared_preload_libraries = 'pgaudit'

# Cấu hình audit
pgaudit.log = 'ddl, role, write'
pgaudit.log_catalog = off
pgaudit.log_parameter = on
pgaudit.log_relation = on
pgaudit.log_statement_once = off

Khởi động lại dịch vụ PostgreSQL để tải shared library:

sudo systemctl restart postgresql

Kết nối tới cơ sở dữ liệu đích và kích hoạt extension:

\c orders_prod
CREATE EXTENSION IF NOT EXISTS pgaudit;

Bước 4: Xác minh và kiểm thử cấu hình bảo mật

Đừng bao giờ mặc định rằng các quy tắc bảo mật hoạt động đúng mà không qua kiểm thử thực tế. Hãy kiểm tra kết nối từ xa bằng cờ xác thực SSL nghiêm ngặt:

# Xác minh kết nối thành công khi bắt buộc dùng SSL
psql "host=10.0.4.50 port=5432 dbname=orders_prod user=api_gateway sslmode=require"

Kiểm tra tính năng ghi audit log bằng cách thực hiện thay đổi bảng bên trong database:

CREATE TABLE audit_check (id serial PRIMARY KEY, verified_at timestamptz DEFAULT now());

Kiểm tra file log PostgreSQL đang hoạt động trong /var/log/postgresql/ để xác minh output có cấu trúc của pgAudit:

LOG:  AUDIT: SESSION,1,1,DDL,CREATE TABLE,TABLE,public.audit_check,CREATE TABLE audit_check (id serial PRIMARY KEY, verified_at timestamptz DEFAULT now());,<not logged>

TLS 1.3 ngăn chặn việc nghe lén dữ liệu trên đường truyền. Các quy tắc pg_hba.conf chặt chẽ chặn đứng hành vi di chuyển ngang (lateral movement). Trong khi đó, pgAudit mang lại dấu vết kiểm toán không thể chối cãi cho mọi truy vấn quan trọng. Kết hợp ba lớp bảo vệ này sẽ biến một mục tiêu dễ bị tổn thương thành một hệ thống vững chắc, sẵn sàng cho môi trường production.

Share: