ข้อมูลในฐานข้อมูลอาจถูกเข้าถึงได้แม้ไม่ผ่าน Application เช่น ขโมยฮาร์ดดิสก์ เข้าถึงไฟล์ Backup ที่ไม่ได้เข้ารหัส หรือเจาะเข้าเซิร์ฟเวอร์แล้วคัดลอกไฟล์ข้อมูลโดยตรง การเข้ารหัสข้อมูลขณะจัดเก็บ (Encryption at Rest) จึงเป็นชั้นป้องกันที่สำคัญ ทำให้แม้ไฟล์ข้อมูลจะถูกขโมยไป ก็ไม่สามารถอ่านเนื้อหาภายในได้
บทความนี้อธิบายวิธีตั้งค่า Encryption at Rest สำหรับ MySQL/MariaDB, PostgreSQL และ MongoDB พร้อมแนวทางจัดการ Key Management อย่างปลอดภัย ครอบคลุมทั้ง Transparent Data Encryption (TDE), Column-level Encryption และการเข้ารหัสไฟล์ Backup
ทำไมต้อง Encrypt ข้อมูลขณะจัดเก็บ
การเข้ารหัสข้อมูลขณะจัดเก็บป้องกันกรณีที่ผู้โจมตีเข้าถึงไฟล์ข้อมูลโดยตรง โดยไม่ผ่าน Database Service ตัวอย่างสถานการณ์ที่ Encryption at Rest ช่วยป้องกันได้ เช่น เซิร์ฟเวอร์ถูกเจาะแล้วคัดลอกไฟล์ Data Directory, ฮาร์ดดิสก์หรือ SSD ถูกขโมยหรือไม่ได้ทำลายก่อนทิ้ง, ไฟล์ Backup ถูกดึงออกจาก Storage, Cloud Snapshot ถูกเข้าถึงจากบัญชีที่ถูก Compromise และข้อกำหนดด้าน Compliance เช่น PDPA, PCI-DSS, HIPAA ที่กำหนดให้เข้ารหัสข้อมูลสำคัญ
MySQL/MariaDB — InnoDB Tablespace Encryption
MySQL และ MariaDB รองรับ Transparent Data Encryption (TDE) สำหรับ InnoDB โดยเข้ารหัสข้อมูลระดับ Tablespace ทำให้แอปพลิเคชันไม่ต้องแก้ไขอะไร เพราะการเข้า-ถอดรหัสเกิดขึ้นโดยอัตโนมัติเมื่ออ่านหรือเขียนข้อมูล
ตั้งค่า Keyring Plugin (MySQL)
# MySQL 8.x — ตั้งค่า Keyring File Plugin
# แก้ไข my.cnf
[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring
# สร้าง Directory สำหรับเก็บ Key
sudo mkdir -p /var/lib/mysql-keyring
sudo chown mysql:mysql /var/lib/mysql-keyring
sudo chmod 750 /var/lib/mysql-keyring
# Restart MySQL
sudo systemctl restart mysql
# ตรวจสอบว่า Plugin ทำงาน
mysql -u root -p -e "SELECT PLUGIN_NAME, PLUGIN_STATUS FROM information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';"
# ต้องแสดง keyring_file | ACTIVE
เปิด Encryption สำหรับตาราง
# เข้ารหัสตารางที่มีอยู่แล้ว
ALTER TABLE mydb.customers ENCRYPTION='Y';
ALTER TABLE mydb.orders ENCRYPTION='Y';
ALTER TABLE mydb.payments ENCRYPTION='Y';
# สร้างตารางใหม่พร้อมเข้ารหัส
CREATE TABLE mydb.sensitive_data (
id INT AUTO_INCREMENT PRIMARY KEY,
ssn VARCHAR(20),
credit_card VARCHAR(30)
) ENCRYPTION='Y';
# ตั้งค่าให้ตารางใหม่เข้ารหัสอัตโนมัติ (MySQL 8.0.16+)
# my.cnf
[mysqld]
default_table_encryption=ON
# ตรวจสอบตารางที่เข้ารหัสแล้ว
SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS
FROM information_schema.tables
WHERE CREATE_OPTIONS LIKE '%ENCRYPTION%';
# เข้ารหัส System Tablespace (redo log, undo log)
ALTER INSTANCE ROTATE INNODB MASTER KEY;
MariaDB — Encryption Plugin
# MariaDB — ใช้ file_key_management Plugin
# สร้าง Encryption Key
openssl rand -hex 32 > /etc/mysql/encryption/keyfile
echo "1;$(cat /etc/mysql/encryption/keyfile)" > /etc/mysql/encryption/keyfile.txt
# เข้ารหัส Key File ด้วย Password
openssl rand -hex 128 > /etc/mysql/encryption/keyfile.key
openssl enc -aes-256-cbc -md sha1 \
-pass file:/etc/mysql/encryption/keyfile.key \
-in /etc/mysql/encryption/keyfile.txt \
-out /etc/mysql/encryption/keyfile.enc
# ตั้งค่า my.cnf
[mysqld]
plugin_load_add = file_key_management
file_key_management_filename = /etc/mysql/encryption/keyfile.enc
file_key_management_filekey = FILE:/etc/mysql/encryption/keyfile.key
file_key_management_encryption_algorithm = AES_CTR
# เข้ารหัสตาราง InnoDB ทั้งหมดอัตโนมัติ
innodb_encrypt_tables = FORCE
innodb_encrypt_log = ON
innodb_encryption_threads = 4
# Restart MariaDB
sudo systemctl restart mariadb
# ตรวจสอบสถานะ
SELECT NAME, ENCRYPTION_SCHEME, CURRENT_KEY_ID
FROM information_schema.INNODB_TABLESPACES_ENCRYPTION;
PostgreSQL — Transparent Data Encryption
PostgreSQL ไม่มี Built-in TDE ในเวอร์ชัน Community แต่มีทางเลือกหลายวิธี ได้แก่ การเข้ารหัสระดับ Column ด้วย pgcrypto, การเข้ารหัสระดับ Filesystem ด้วย LUKS หรือ dm-crypt และการใช้ PostgreSQL ที่มี TDE Extension จาก Third-party เช่น Percona หรือ CyberTec
pgcrypto — Column-level Encryption
# เปิดใช้ pgcrypto Extension
CREATE EXTENSION IF NOT EXISTS pgcrypto;
# เข้ารหัสแบบ Symmetric (AES-256)
-- สร้างตาราง
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
ssn BYTEA -- เก็บข้อมูลเข้ารหัสเป็น BYTEA
);
-- Insert ข้อมูลพร้อมเข้ารหัส
INSERT INTO customers (name, email, ssn)
VALUES (
'John Doe',
'[email protected]',
pgp_sym_encrypt('123-45-6789', 'MySecretEncryptionKey2026!')
);
-- อ่านข้อมูลพร้อมถอดรหัส
SELECT name, email,
pgp_sym_decrypt(ssn, 'MySecretEncryptionKey2026!') AS ssn_decrypted
FROM customers;
# เข้ารหัสแบบ Asymmetric (RSA)
-- สร้าง Key Pair ก่อน
-- gpg --gen-key
-- gpg --export -a "DBKey" > public.key
-- gpg --export-secret-keys -a "DBKey" > private.key
-- เข้ารหัสด้วย Public Key
INSERT INTO customers (name, email, ssn)
VALUES (
'Jane Smith',
'[email protected]',
pgp_pub_encrypt('987-65-4321', dearmor(pg_read_file('public.key')))
);
-- ถอดรหัสด้วย Private Key
SELECT name, email,
pgp_pub_decrypt(ssn, dearmor(pg_read_file('private.key'))) AS ssn_decrypted
FROM customers;
Filesystem-level Encryption (LUKS)
# ใช้ LUKS เข้ารหัส Partition ที่เก็บ PostgreSQL Data
# วิธีนี้เข้ารหัสทุกอย่างบน Partition รวมถึง WAL, Temp files, Index
# 1. สร้าง Encrypted Partition
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 pgdata_crypt
# 2. สร้าง Filesystem
sudo mkfs.ext4 /dev/mapper/pgdata_crypt
# 3. Mount
sudo mkdir -p /var/lib/postgresql/data_encrypted
sudo mount /dev/mapper/pgdata_crypt /var/lib/postgresql/data_encrypted
sudo chown postgres:postgres /var/lib/postgresql/data_encrypted
# 4. ย้าย PostgreSQL Data Directory
sudo systemctl stop postgresql
sudo rsync -av /var/lib/postgresql/16/main/ /var/lib/postgresql/data_encrypted/
# 5. แก้ไข postgresql.conf
# data_directory = '/var/lib/postgresql/data_encrypted'
# 6. ตั้งค่า Auto-unlock (ใช้ Key File)
sudo dd if=/dev/urandom of=/root/.pgdata_key bs=1024 count=4
sudo chmod 400 /root/.pgdata_key
sudo cryptsetup luksAddKey /dev/sdb1 /root/.pgdata_key
# /etc/crypttab
# pgdata_crypt /dev/sdb1 /root/.pgdata_key luks
sudo systemctl start postgresql
MongoDB — Encryption at Rest
MongoDB Enterprise รองรับ Encryption at Rest แบบ Built-in โดยใช้ WiredTiger Storage Engine เข้ารหัสไฟล์ข้อมูลทั้งหมดด้วย AES-256 ส่วน MongoDB Community สามารถใช้ Filesystem-level Encryption แทนได้
MongoDB Enterprise — Built-in Encryption
# MongoDB Enterprise — ตั้งค่า Encryption at Rest
# mongod.conf
security:
enableEncryption: true
encryptionCipherMode: AES256-CBC
encryptionKeyFile: /etc/mongodb/encryption-keyfile
# สร้าง Encryption Key (256-bit)
openssl rand -base64 32 > /etc/mongodb/encryption-keyfile
sudo chmod 600 /etc/mongodb/encryption-keyfile
sudo chown mongod:mongod /etc/mongodb/encryption-keyfile
# Restart MongoDB
sudo systemctl restart mongod
# ตรวจสอบว่า Encryption เปิดอยู่
mongosh -u adminUser -p 'password' --authenticationDatabase admin --eval "
db.serverStatus().encryptionAtRest
"
MongoDB Enterprise — KMIP Integration
# ใช้ KMIP Server สำหรับ Key Management (แนะนำสำหรับ Production)
# mongod.conf
security:
enableEncryption: true
kmip:
serverName: kmip.example.com
port: 5696
clientCertificateFile: /etc/mongodb/kmip-client.pem
serverCAFile: /etc/mongodb/kmip-ca.pem
# KMIP Server ที่นิยมใช้:
# - HashiCorp Vault (Enterprise)
# - AWS KMS
# - Azure Key Vault
# - Google Cloud KMS
MongoDB Community — Filesystem Encryption
# MongoDB Community ไม่มี Built-in Encryption
# ใช้ LUKS เข้ารหัส Data Directory แทน
# 1. สร้าง Encrypted Volume
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 mongodata_crypt
# 2. สร้าง Filesystem และ Mount
sudo mkfs.xfs /dev/mapper/mongodata_crypt
sudo mkdir -p /var/lib/mongodb_encrypted
sudo mount /dev/mapper/mongodata_crypt /var/lib/mongodb_encrypted
sudo chown mongod:mongod /var/lib/mongodb_encrypted
# 3. แก้ไข mongod.conf
# storage:
# dbPath: /var/lib/mongodb_encrypted
# 4. Restart
sudo systemctl restart mongod
Application-level Encryption
นอกจาก Encryption at Rest ระดับฐานข้อมูล การเข้ารหัสระดับ Application เป็นอีกทางเลือก โดยเข้ารหัสข้อมูลก่อนส่งไปเก็บในฐานข้อมูล ทำให้แม้ Database Administrator หรือผู้ที่เข้าถึงฐานข้อมูลโดยตรงก็ไม่สามารถอ่านข้อมูลได้
# Python — เข้ารหัสด้วย cryptography library
from cryptography.fernet import Fernet
import mysql.connector
# สร้าง Key (เก็บใน Vault หรือ Environment Variable)
# key = Fernet.generate_key()
key = b'your-fernet-key-stored-in-vault'
cipher = Fernet(key)
# เข้ารหัสก่อน Insert
ssn = '123-45-6789'
encrypted_ssn = cipher.encrypt(ssn.encode())
conn = mysql.connector.connect(host='localhost', user='app', password='pass', database='mydb')
cursor = conn.cursor()
cursor.execute(
"INSERT INTO customers (name, email, ssn_encrypted) VALUES (%s, %s, %s)",
('John Doe', '[email protected]', encrypted_ssn)
)
conn.commit()
# ถอดรหัสหลัง Select
cursor.execute("SELECT name, email, ssn_encrypted FROM customers WHERE id = %s", (1,))
row = cursor.fetchone()
decrypted_ssn = cipher.decrypt(row[2]).decode()
print(f"Name: {row[0]}, SSN: {decrypted_ssn}")
# Node.js — เข้ารหัสด้วย crypto module
# const crypto = require('crypto');
# const algorithm = 'aes-256-gcm';
# const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
#
# function encrypt(text) {
# const iv = crypto.randomBytes(16);
# const cipher = crypto.createCipheriv(algorithm, key, iv);
# let encrypted = cipher.update(text, 'utf8', 'hex');
# encrypted += cipher.final('hex');
# const tag = cipher.getAuthTag().toString('hex');
# return iv.toString('hex') + ':' + encrypted + ':' + tag;
# }
#
# function decrypt(data) {
# const [ivHex, encryptedHex, tagHex] = data.split(':');
# const iv = Buffer.from(ivHex, 'hex');
# const encrypted = Buffer.from(encryptedHex, 'hex');
# const tag = Buffer.from(tagHex, 'hex');
# const decipher = crypto.createDecipheriv(algorithm, key, iv);
# decipher.setAuthTag(tag);
# let decrypted = decipher.update(encrypted, null, 'utf8');
# decrypted += decipher.final('utf8');
# return decrypted;
# }
Key Management Best Practices
การจัดการ Encryption Key เป็นส่วนสำคัญที่สุดของ Encryption at Rest ถ้า Key ถูกขโมยหรือสูญหาย การเข้ารหัสก็ไม่มีประโยชน์ หรืออาจทำให้ข้อมูลสูญหายถาวร
# === หลักการ Key Management ===
# 1. แยก Key ออกจาก Data เสมอ
# ❌ ผิด — เก็บ Key บนเซิร์ฟเวอร์เดียวกับ Database
# /var/lib/mysql/encryption-key (อยู่ข้างๆ Data)
# ✅ ถูก — เก็บ Key แยกต่างหาก
# - HashiCorp Vault
# - AWS KMS / Azure Key Vault / GCP KMS
# - Hardware Security Module (HSM)
# - เซิร์ฟเวอร์แยกที่ Access จำกัด
# 2. Rotate Key เป็นประจำ
# MySQL — Rotate Master Key
ALTER INSTANCE ROTATE INNODB MASTER KEY;
# MariaDB — Rotate Key
# เปลี่ยน Key ใน keyfile แล้ว:
SET GLOBAL innodb_encryption_rotate_key_age = 1;
# MariaDB จะ Re-encrypt ตารางด้วย Key ใหม่อัตโนมัติ
# 3. ตั้ง Permission ให้ Key File เข้มงวด
sudo chmod 400 /etc/mysql/encryption/keyfile
sudo chown mysql:mysql /etc/mysql/encryption/keyfile
# เฉพาะ mysql user เท่านั้นที่อ่านได้
# 4. Backup Key แยกจาก Backup Data
# ❌ ผิด — เก็บ Key ไว้ใน Backup เดียวกับ Data
# ✅ ถูก — Backup Key ไปที่ต่างที่ ต่าง Storage
# === HashiCorp Vault — ตัวอย่างการเก็บ Key ===
# เปิดใช้ Transit Secret Engine
# vault secrets enable transit
# vault write -f transit/keys/mysql-encryption type=aes256-gcm96
# เข้ารหัส Data Key ด้วย Vault
# vault write transit/encrypt/mysql-encryption plaintext=$(base64 <<< "actual-key")
# ถอดรหัส Data Key
# vault write transit/decrypt/mysql-encryption ciphertext="vault:v1:..."
เข้ารหัส Backup
Backup ที่ไม่ได้เข้ารหัสเป็นช่องโหว่ที่พบบ่อย แม้ฐานข้อมูลจะเข้ารหัสแล้ว แต่เมื่อ Export ข้อมูลออกมาเป็นไฟล์ Backup ข้อมูลจะอยู่ในรูปแบบ Plain Text ต้องเข้ารหัส Backup แยกต่างหากเสมอ
# === MySQL — Backup พร้อมเข้ารหัส ===
# วิธีที่ 1: ใช้ OpenSSL
mysqldump -u root --all-databases | openssl enc -aes-256-cbc -pbkdf2 \
-pass file:/root/.backup_key -out /backup/mysql_$(date +%Y%m%d).sql.enc
# ถอดรหัส Backup
openssl enc -d -aes-256-cbc -pbkdf2 \
-pass file:/root/.backup_key -in /backup/mysql_20260407.sql.enc | mysql -u root
# วิธีที่ 2: ใช้ GPG
mysqldump -u root --all-databases | gpg --encrypt --recipient [email protected] \
-o /backup/mysql_$(date +%Y%m%d).sql.gpg
# ถอดรหัส
gpg --decrypt /backup/mysql_20260407.sql.gpg | mysql -u root
# === PostgreSQL — Backup พร้อมเข้ารหัส ===
pg_dump -U postgres mydb | openssl enc -aes-256-cbc -pbkdf2 \
-pass file:/root/.backup_key -out /backup/pg_mydb_$(date +%Y%m%d).dump.enc
# ถอดรหัส
openssl enc -d -aes-256-cbc -pbkdf2 \
-pass file:/root/.backup_key -in /backup/pg_mydb_20260407.dump.enc | psql -U postgres mydb
# === MongoDB — Backup พร้อมเข้ารหัส ===
mongodump --uri="mongodb://adminUser:password@localhost:27017" \
--archive | openssl enc -aes-256-cbc -pbkdf2 \
-pass file:/root/.backup_key -out /backup/mongo_$(date +%Y%m%d).archive.enc
# ถอดรหัส
openssl enc -d -aes-256-cbc -pbkdf2 \
-pass file:/root/.backup_key -in /backup/mongo_20260407.archive.enc | \
mongorestore --uri="mongodb://adminUser:password@localhost:27017" --archive
ตรวจสอบสถานะ Encryption
# === MySQL — ตรวจสอบ Encryption ===
# ตรวจตารางที่เข้ารหัส
SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS
FROM information_schema.tables
WHERE CREATE_OPTIONS LIKE '%ENCRYPTION="Y"%';
# ตรวจ Keyring Plugin
SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM information_schema.plugins
WHERE PLUGIN_NAME LIKE 'keyring%';
# ตรวจ Encryption Status ของ Tablespace
SELECT SPACE, NAME, FLAG, ENCRYPTION
FROM information_schema.innodb_tablespaces
WHERE ENCRYPTION = 'Y';
# === MariaDB — ตรวจสอบ Encryption ===
SELECT NAME, ENCRYPTION_SCHEME, CURRENT_KEY_ID, KEY_ROTATION_PAGE_NUMBER
FROM information_schema.INNODB_TABLESPACES_ENCRYPTION;
# ตรวจ Variable
SHOW GLOBAL VARIABLES LIKE 'innodb_encrypt%';
# === PostgreSQL — ตรวจ pgcrypto ===
SELECT * FROM pg_extension WHERE extname = 'pgcrypto';
# ตรวจ Column ที่เป็น BYTEA (น่าจะเข้ารหัส)
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE data_type = 'bytea' AND table_schema = 'public';
# === MongoDB — ตรวจ Encryption ===
mongosh -u adminUser -p 'password' --authenticationDatabase admin --eval "
const status = db.serverStatus();
if (status.encryptionAtRest) {
print('Encryption at Rest: ENABLED');
print(JSON.stringify(status.encryptionAtRest, null, 2));
} else {
print('Encryption at Rest: NOT ENABLED');
}"
Performance Impact
การเข้ารหัสมี Overhead ด้าน CPU แต่ในเซิร์ฟเวอร์สมัยใหม่ที่รองรับ AES-NI (Hardware Acceleration) ผลกระทบจะน้อยมาก โดยทั่วไปอยู่ที่ 3-10% ขึ้นอยู่กับ Workload
# ตรวจสอบว่าเซิร์ฟเวอร์รองรับ AES-NI
grep -o aes /proc/cpuinfo | head -1
# ถ้าแสดง "aes" = รองรับ Hardware Acceleration
# Benchmark เปรียบเทียบ (MySQL)
# ก่อนเปิด Encryption
sysbench /usr/share/sysbench/oltp_read_write.lua \
--mysql-host=localhost --mysql-user=root --mysql-db=test \
--tables=10 --table-size=100000 --threads=4 --time=60 run
# หลังเปิด Encryption — รันเหมือนเดิม เปรียบเทียบ TPS
# ผลทั่วไป:
# AES-NI enabled: TPS ลดลง 3-5%
# AES-NI disabled: TPS ลดลง 10-25%
# Read-heavy: ผลกระทบน้อย (data cache ใน Buffer Pool ไม่เข้ารหัส)
# Write-heavy: ผลกระทบมากกว่า (ต้องเข้ารหัสก่อนเขียน Disk)
สรุป
Encryption at Rest เป็นชั้นป้องกันที่สำคัญสำหรับข้อมูลในฐานข้อมูล MySQL/MariaDB รองรับ TDE ผ่าน InnoDB Tablespace Encryption ที่ตั้งค่าได้ง่ายและไม่ต้องแก้ไขแอปพลิเคชัน PostgreSQL ใช้ pgcrypto สำหรับ Column-level Encryption หรือ LUKS สำหรับ Filesystem-level Encryption ส่วน MongoDB Enterprise มี Built-in Encryption ที่ทำงานร่วมกับ KMIP Server ได้ สิ่งสำคัญที่สุดคือการจัดการ Encryption Key อย่างถูกต้อง แยก Key ออกจาก Data เสมอ Rotate Key เป็นประจำ และอย่าลืมเข้ารหัส Backup ด้วย
แนะนำบริการ DE
การตั้งค่า Encryption at Rest ต้องการเซิร์ฟเวอร์ที่มี Root Access สำหรับติดตั้ง Keyring Plugin, ตั้งค่า LUKS, และจัดการ Key File ได้อย่างอิสระ Cloud VPS ของ DE รองรับ CPU ที่มี AES-NI สำหรับ Hardware Acceleration ทำให้ Encryption ไม่กระทบ Performance มาก พร้อม SSD NVMe ที่รองรับ I/O สูงแม้เปิด Encryption
สำหรับโปรเจกต์ที่ต้องการความสะดวกและไม่ต้องจัดการ Encryption เอง Cloud Hosting ของ DE มี Managed Infrastructure ที่ดูแลความปลอดภัยระดับพื้นฐานให้พร้อมใช้งาน

