การเชื่อมต่อฐานข้อมูลผ่านเครือข่ายโดยไม่เข้ารหัส ทำให้ข้อมูลที่ส่งระหว่าง Application กับ Database Server อาจถูกดักจับได้ (Man-in-the-Middle Attack) ผู้โจมตีสามารถอ่าน Query, ผลลัพธ์, รวมถึง Username และ Password ที่ส่งในระหว่างการ Authentication ได้ทั้งหมด
บทความนี้อธิบายวิธีตั้งค่า SSL/TLS สำหรับ MySQL/MariaDB, PostgreSQL และ MongoDB ตั้งแต่การสร้าง Certificate การตั้งค่าฝั่ง Server การเชื่อมต่อจากฝั่ง Client และการบังคับให้ทุกการเชื่อมต่อต้องใช้ SSL เท่านั้น
ทำไมต้องใช้ SSL/TLS สำหรับ Database
SSL/TLS เข้ารหัสข้อมูลที่ส่งระหว่าง Client กับ Database Server ทำให้แม้มีคนดักจับ Network Traffic ก็ไม่สามารถอ่านข้อมูลได้ นอกจากนี้ยังช่วยยืนยันตัวตนของ Server ว่าเป็นเซิร์ฟเวอร์ที่ถูกต้อง ไม่ใช่เซิร์ฟเวอร์ปลอมที่ผู้โจมตีตั้งขึ้น กรณีที่ต้องใช้ SSL เช่น Application กับ Database อยู่คนละเซิร์ฟเวอร์, เชื่อมต่อข้าม Data Center หรือข้าม Cloud Region, ข้อกำหนด Compliance เช่น PCI-DSS หรือ PDPA กำหนดให้เข้ารหัสข้อมูลระหว่างส่ง และการเชื่อมต่อจากภายนอกเข้ามาจัดการฐานข้อมูลผ่านอินเทอร์เน็ต
สร้าง SSL Certificate
ขั้นตอนแรกคือสร้าง Certificate สำหรับใช้กับ Database Server โดยสามารถใช้ Self-signed Certificate สำหรับ Internal Use หรือ Certificate จาก CA ที่เชื่อถือได้สำหรับ Production
Self-signed Certificate
# สร้าง CA (Certificate Authority) ของตัวเอง
openssl genrsa 4096 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem \
-out ca-cert.pem -subj "/CN=DB-CA/O=MyOrg"
# สร้าง Server Certificate
openssl req -newkey rsa:4096 -nodes -keyout server-key.pem \
-out server-req.pem -subj "/CN=db-server/O=MyOrg"
openssl x509 -req -in server-req.pem -days 3650 \
-CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem
# สร้าง Client Certificate (สำหรับ Mutual TLS)
openssl req -newkey rsa:4096 -nodes -keyout client-key.pem \
-out client-req.pem -subj "/CN=db-client/O=MyOrg"
openssl x509 -req -in client-req.pem -days 3650 \
-CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \
-out client-cert.pem
# ตรวจสอบ Certificate
openssl verify -CAfile ca-cert.pem server-cert.pem
openssl verify -CAfile ca-cert.pem client-cert.pem
# ต้องแสดง: OK
# ตั้ง Permission
chmod 600 server-key.pem client-key.pem ca-key.pem
chmod 644 server-cert.pem client-cert.pem ca-cert.pem
Certificate จาก Let’s Encrypt
# ใช้ Let's Encrypt สำหรับ Database ที่เปิดรับจากภายนอก
# ต้องมี Domain Name ชี้มาที่เซิร์ฟเวอร์
sudo certbot certonly --standalone -d db.example.com
# ไฟล์ Certificate จะอยู่ที่:
# /etc/letsencrypt/live/db.example.com/fullchain.pem (Server Cert + CA)
# /etc/letsencrypt/live/db.example.com/privkey.pem (Server Key)
# Copy ไปใช้กับ Database (ตั้ง Permission ให้ถูกต้อง)
sudo cp /etc/letsencrypt/live/db.example.com/fullchain.pem /etc/mysql/ssl/server-cert.pem
sudo cp /etc/letsencrypt/live/db.example.com/privkey.pem /etc/mysql/ssl/server-key.pem
sudo chown mysql:mysql /etc/mysql/ssl/*.pem
sudo chmod 600 /etc/mysql/ssl/server-key.pem
# ตั้ง Cron สำหรับ Renew อัตโนมัติ (ทุก 60 วัน)
# 0 3 1 */2 * certbot renew --post-hook "systemctl restart mysql"
MySQL/MariaDB — ตั้งค่า SSL
ตั้งค่าฝั่ง Server
# คัดลอก Certificate ไปยัง MySQL Directory
sudo mkdir -p /etc/mysql/ssl
sudo cp ca-cert.pem server-cert.pem server-key.pem /etc/mysql/ssl/
sudo chown mysql:mysql /etc/mysql/ssl/*
sudo chmod 600 /etc/mysql/ssl/server-key.pem
# แก้ไข 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
# กำหนด TLS Version ขั้นต่ำ (แนะนำ TLSv1.2 ขึ้นไป)
tls_version=TLSv1.2,TLSv1.3
# Restart MySQL
sudo systemctl restart mysql
# ตรวจสอบว่า SSL เปิดอยู่
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
# have_ssl = YES
# ssl_ca = /etc/mysql/ssl/ca-cert.pem
# ssl_cert = /etc/mysql/ssl/server-cert.pem
# ssl_key = /etc/mysql/ssl/server-key.pem
บังคับให้ User ต้องใช้ SSL
# สร้าง User ที่บังคับ SSL
CREATE USER 'appuser'@'%' IDENTIFIED BY 'StrongP@ss2026!'
REQUIRE SSL;
# บังคับ SSL พร้อม Client Certificate (Mutual TLS)
CREATE USER 'secureuser'@'%' IDENTIFIED BY 'StrongP@ss2026!'
REQUIRE X509;
# บังคับ SSL เฉพาะ Issuer/Subject
CREATE USER 'strictuser'@'%' IDENTIFIED BY 'StrongP@ss2026!'
REQUIRE SUBJECT '/CN=db-client/O=MyOrg'
AND ISSUER '/CN=DB-CA/O=MyOrg';
# แก้ไข User ที่มีอยู่ให้บังคับ SSL
ALTER USER 'existinguser'@'%' REQUIRE SSL;
# บังคับ SSL สำหรับทุก User (Global)
# my.cnf
[mysqld]
require_secure_transport=ON
# ตรวจสอบ SSL Status ของ Connection ปัจจุบัน
mysql -u root -p -e "SHOW STATUS LIKE 'Ssl_cipher';"
# ถ้าแสดง cipher name = กำลังใช้ SSL
# ถ้าว่าง = ไม่ได้ใช้ SSL
เชื่อมต่อจาก Client ด้วย SSL
# เชื่อมต่อผ่าน mysql client พร้อม SSL
mysql -u appuser -p \
--ssl-ca=/path/to/ca-cert.pem \
--ssl-cert=/path/to/client-cert.pem \
--ssl-key=/path/to/client-key.pem \
-h db-server.example.com
# ตรวจสอบว่าเชื่อมต่อด้วย SSL
mysql> \s
# SSL: Cipher in use is TLS_AES_256_GCM_SHA384
# Python — เชื่อมต่อ MySQL ด้วย SSL
# import mysql.connector
#
# conn = mysql.connector.connect(
# host='db-server.example.com',
# user='appuser',
# password='StrongP@ss2026!',
# database='mydb',
# ssl_ca='/path/to/ca-cert.pem',
# ssl_cert='/path/to/client-cert.pem',
# ssl_key='/path/to/client-key.pem'
# )
# Node.js — เชื่อมต่อ MySQL ด้วย SSL
# const mysql = require('mysql2');
# const fs = require('fs');
#
# const conn = mysql.createConnection({
# host: 'db-server.example.com',
# user: 'appuser',
# password: 'StrongP@ss2026!',
# database: 'mydb',
# ssl: {
# ca: fs.readFileSync('/path/to/ca-cert.pem'),
# cert: fs.readFileSync('/path/to/client-cert.pem'),
# key: fs.readFileSync('/path/to/client-key.pem')
# }
# });
PostgreSQL — ตั้งค่า SSL
ตั้งค่าฝั่ง Server
# คัดลอก Certificate ไปยัง PostgreSQL Data Directory
sudo cp server-cert.pem /var/lib/postgresql/16/main/server.crt
sudo cp server-key.pem /var/lib/postgresql/16/main/server.key
sudo cp ca-cert.pem /var/lib/postgresql/16/main/root.crt
sudo chown postgres:postgres /var/lib/postgresql/16/main/server.*
sudo chown postgres:postgres /var/lib/postgresql/16/main/root.crt
sudo chmod 600 /var/lib/postgresql/16/main/server.key
# แก้ไข postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ca_file = 'root.crt'
ssl_min_protocol_version = 'TLSv1.2'
# แก้ไข pg_hba.conf — บังคับ SSL สำหรับ Remote Connection
# TYPE DATABASE USER ADDRESS METHOD
hostssl all all 0.0.0.0/0 scram-sha-256
hostssl all all ::/0 scram-sha-256
# ถ้าต้องการ Mutual TLS (Client Certificate)
hostssl all all 0.0.0.0/0 cert
# Restart PostgreSQL
sudo systemctl restart postgresql
# ตรวจสอบว่า SSL เปิดอยู่
psql -U postgres -c "SHOW ssl;"
# ssl = on
เชื่อมต่อจาก Client ด้วย SSL
# เชื่อมต่อผ่าน psql พร้อม SSL
psql "host=db-server.example.com dbname=mydb user=appuser \
sslmode=verify-full \
sslcert=/path/to/client-cert.pem \
sslkey=/path/to/client-key.pem \
sslrootcert=/path/to/ca-cert.pem"
# SSL Mode Options:
# disable — ไม่ใช้ SSL
# allow — ใช้ SSL ถ้า Server รองรับ
# prefer — ใช้ SSL ถ้าได้ (default)
# require — บังคับ SSL แต่ไม่ตรวจ Certificate
# verify-ca — บังคับ SSL + ตรวจ CA
# verify-full — บังคับ SSL + ตรวจ CA + ตรวจ Hostname (แนะนำ)
# ตรวจสอบ SSL Status หลังเชื่อมต่อ
psql> SELECT ssl_is_used();
# ต้องได้ true
# Python — เชื่อมต่อ PostgreSQL ด้วย SSL (psycopg2)
# import psycopg2
#
# conn = psycopg2.connect(
# host='db-server.example.com',
# dbname='mydb',
# user='appuser',
# password='StrongP@ss2026!',
# sslmode='verify-full',
# sslcert='/path/to/client-cert.pem',
# sslkey='/path/to/client-key.pem',
# sslrootcert='/path/to/ca-cert.pem'
# )
# Node.js — เชื่อมต่อ PostgreSQL ด้วย SSL (pg)
# const { Client } = require('pg');
# const fs = require('fs');
#
# const client = new Client({
# host: 'db-server.example.com',
# database: 'mydb',
# user: 'appuser',
# password: 'StrongP@ss2026!',
# ssl: {
# rejectUnauthorized: true,
# ca: fs.readFileSync('/path/to/ca-cert.pem').toString(),
# cert: fs.readFileSync('/path/to/client-cert.pem').toString(),
# key: fs.readFileSync('/path/to/client-key.pem').toString()
# }
# });
MongoDB — ตั้งค่า TLS
ตั้งค่าฝั่ง Server
# MongoDB ใช้ไฟล์ PEM ที่รวม Certificate + Key ไว้ด้วยกัน
cat server-cert.pem server-key.pem > /etc/mongodb/ssl/server.pem
sudo chmod 600 /etc/mongodb/ssl/server.pem
sudo chown mongod:mongod /etc/mongodb/ssl/server.pem
sudo cp ca-cert.pem /etc/mongodb/ssl/ca.pem
# แก้ไข mongod.conf
net:
tls:
mode: requireTLS
certificateKeyFile: /etc/mongodb/ssl/server.pem
CAFile: /etc/mongodb/ssl/ca.pem
# TLS Mode Options:
# disabled — ไม่ใช้ TLS
# allowTLS — รับทั้ง TLS และ Non-TLS
# preferTLS — ใช้ TLS ถ้าได้
# requireTLS — บังคับ TLS ทุกการเชื่อมต่อ (แนะนำ)
# Restart MongoDB
sudo systemctl restart mongod
# ตรวจสอบว่า TLS เปิดอยู่
mongosh --tls \
--tlsCAFile /etc/mongodb/ssl/ca.pem \
--tlsCertificateKeyFile /path/to/client.pem \
-u adminUser -p 'password' --authenticationDatabase admin \
--eval "db.adminCommand({ getParameter: 1, tlsMode: 1 })"
เชื่อมต่อจาก Client ด้วย TLS
# เชื่อมต่อผ่าน mongosh พร้อม TLS
mongosh "mongodb://appuser:[email protected]:27017/mydb" \
--tls \
--tlsCAFile /path/to/ca-cert.pem \
--tlsCertificateKeyFile /path/to/client.pem
# Connection String สำหรับ Application
# mongodb://appuser:[email protected]:27017/mydb?tls=true&tlsCAFile=/path/to/ca.pem
# Python — เชื่อมต่อ MongoDB ด้วย TLS (pymongo)
# from pymongo import MongoClient
#
# client = MongoClient(
# 'mongodb://db-server.example.com:27017/',
# username='appuser',
# password='password',
# authSource='mydb',
# tls=True,
# tlsCAFile='/path/to/ca-cert.pem',
# tlsCertificateKeyFile='/path/to/client.pem'
# )
# Node.js — เชื่อมต่อ MongoDB ด้วย TLS
# const { MongoClient } = require('mongodb');
# const fs = require('fs');
#
# const client = new MongoClient(
# 'mongodb://db-server.example.com:27017/mydb', {
# auth: { username: 'appuser', password: 'password' },
# tls: true,
# tlsCAFile: '/path/to/ca-cert.pem',
# tlsCertificateKeyFile: '/path/to/client.pem'
# });
ตรวจสอบและ Debug SSL/TLS
# === ตรวจสอบ SSL ของ Database Server ด้วย openssl ===
# MySQL (Port 3306)
openssl s_client -connect db-server.example.com:3306 -starttls mysql
# ดู Certificate chain, Protocol, Cipher
# PostgreSQL (Port 5432)
openssl s_client -connect db-server.example.com:5432 -starttls postgres
# MongoDB (Port 27017)
openssl s_client -connect db-server.example.com:27017
# === ตรวจ TLS Version ที่ใช้งาน ===
# MySQL
mysql -u root -p -e "
SELECT * FROM performance_schema.session_status
WHERE VARIABLE_NAME IN ('Ssl_version', 'Ssl_cipher');"
# PostgreSQL
psql -U postgres -c "
SELECT datname, usename, ssl, version, cipher
FROM pg_stat_ssl JOIN pg_stat_activity
ON pg_stat_ssl.pid = pg_stat_activity.pid;"
# MongoDB
mongosh --tls --tlsCAFile ca.pem -u adminUser -p 'password' \
--authenticationDatabase admin --eval "
db.currentOp().inprog.forEach(op => {
if (op.client) print(op.client, op.connectionId);
});"
# === ตรวจ Certificate หมดอายุ ===
openssl x509 -in server-cert.pem -noout -dates
# notBefore=Apr 7 00:00:00 2026 GMT
# notAfter=Apr 5 00:00:00 2036 GMT
# ตั้ง Alert เตือนก่อน Certificate หมดอายุ
# Script ตรวจสอบ (ใช้ร่วมกับ Cron)
# EXPIRY=$(openssl x509 -in /etc/mysql/ssl/server-cert.pem -noout -enddate | cut -d= -f2)
# EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
# NOW_EPOCH=$(date +%s)
# DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
# if [ "$DAYS_LEFT" -lt 30 ]; then
# echo "WARNING: SSL Certificate expires in $DAYS_LEFT days" | mail -s "SSL Alert" [email protected]
# fi
Certificate Rotation
Certificate มีอายุจำกัด เมื่อใกล้หมดอายุต้อง Renew และเปลี่ยน Certificate โดยไม่กระทบ Service ขั้นตอนการ Rotate Certificate มีดังนี้
# === ขั้นตอน Certificate Rotation ===
# 1. สร้าง Certificate ชุดใหม่ (ใช้ CA เดิม)
openssl req -newkey rsa:4096 -nodes -keyout server-key-new.pem \
-out server-req-new.pem -subj "/CN=db-server/O=MyOrg"
openssl x509 -req -in server-req-new.pem -days 3650 \
-CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert-new.pem
# 2. ตรวจสอบ Certificate ใหม่
openssl verify -CAfile ca-cert.pem server-cert-new.pem
# 3. วาง Certificate ใหม่ (MySQL)
sudo cp server-cert-new.pem /etc/mysql/ssl/server-cert.pem
sudo cp server-key-new.pem /etc/mysql/ssl/server-key.pem
sudo chown mysql:mysql /etc/mysql/ssl/*.pem
sudo chmod 600 /etc/mysql/ssl/server-key.pem
# 4. Reload โดยไม่ Restart (MySQL 8.0+)
mysql -u root -p -e "ALTER INSTANCE RELOAD TLS;"
# ไม่ต้อง Restart — Connection ที่มีอยู่จะยังใช้ Certificate เดิม
# Connection ใหม่จะใช้ Certificate ใหม่
# PostgreSQL — Reload
sudo systemctl reload postgresql
# หรือ: SELECT pg_reload_conf();
# MongoDB — ต้อง Restart
sudo systemctl restart mongod
# 5. ตรวจสอบว่า Certificate ใหม่ถูกใช้
openssl s_client -connect localhost:3306 -starttls mysql 2>/dev/null | \
openssl x509 -noout -dates
Best Practices สำหรับ Database SSL
การตั้งค่า SSL/TLS ให้ปลอดภัยต้องคำนึงถึงหลายปัจจัย ตั้งแต่ TLS Version ที่ใช้ ไปจนถึงการจัดการ Certificate อย่างถูกต้อง แนวปฏิบัติที่ควรทำ ได้แก่ ใช้ TLS 1.2 ขึ้นไปเท่านั้นเพราะ TLS 1.0 และ 1.1 มีช่องโหว่ที่รู้จักแล้ว, ใช้ verify-full หรือ requireTLS แทน require เพื่อป้องกัน MITM, ตั้ง Certificate อายุไม่เกิน 1 ปี สำหรับ Production และ Rotate ก่อนหมดอายุ, เก็บ Private Key ให้ Permission เข้มงวด (chmod 600) เฉพาะ Database User เท่านั้นที่อ่านได้, แยก CA Certificate ไปเก็บที่ปลอดภัยเหมือน Encryption Key, Monitor Certificate Expiry ด้วย Cron Job หรือ Monitoring Tool เช่น Prometheus, และใช้ Mutual TLS (Client Certificate) สำหรับ Connection ที่สำคัญมาก
สรุป
SSL/TLS เป็นพื้นฐานด้านความปลอดภัยที่ Database Server ทุกตัวควรเปิดใช้ MySQL/MariaDB ตั้งค่าผ่าน my.cnf ด้วย ssl-ca, ssl-cert, ssl-key และบังคับด้วย REQUIRE SSL ส่วน PostgreSQL ใช้ postgresql.conf ร่วมกับ pg_hba.conf โดยมี sslmode ให้เลือกหลายระดับ และ MongoDB ตั้งค่าผ่าน mongod.conf ด้วย net.tls สิ่งสำคัญคือต้องบังคับให้ทุกการเชื่อมต่อใช้ SSL เสมอ ไม่ใช่แค่เปิดรองรับ และต้อง Monitor Certificate Expiry เพื่อ Rotate ก่อนหมดอายุ
แนะนำบริการ DE
การตั้งค่า SSL/TLS สำหรับฐานข้อมูลต้องการเซิร์ฟเวอร์ที่มี Root Access สำหรับจัดการ Certificate และตั้งค่า Configuration File ได้อย่างอิสระ Cloud VPS ของ DE รองรับการติดตั้งและตั้งค่า SSL Certificate สำหรับทุก Database Engine พร้อม Network ที่เสถียรสำหรับ Encrypted Connection
สำหรับโปรเจกต์ที่ต้องการความสะดวกโดยไม่ต้องจัดการ SSL Certificate เอง Cloud Hosting ของ DE มี Managed Infrastructure ที่ดูแลความปลอดภัยของการเชื่อมต่อให้พร้อมใช้งาน

