Thông tin lưu trong database thường rất giá trị với công ty. Nhưng tiếc là nó cũng hấp dẫn với mấy ông hacker. Đó là lý do tại sao nên nghĩ đến mã hóa — đây là một trong những cách bảo vệ dữ liệu khỏi những người không nên xem nó.
Mã hóa giúp bảo vệ thông tin nhạy cảm: ví dụ như mật khẩu, số thẻ tín dụng hoặc dữ liệu cá nhân. Nó cũng giúp tuân thủ các luật như GDPR hay HIPAA. Nếu chẳng may bị lộ dữ liệu, dữ liệu đã mã hóa sẽ ít bị tổn thương hơn nhiều, giảm thiểu thiệt hại tiềm năng.
Trong PostgreSQL có mấy hàm tiện để mã hóa đối xứng. Dùng pgp_sym_encrypt(data, key) bạn có thể mã hóa dữ liệu cần thiết, rồi giải mã lại bằng pgp_sym_decrypt(encrypted_data, key) với cùng một key. Đơn giản mà lại an toàn.
Ví dụ mã hóa dữ liệu
Bước 1: Tạo bảng
Tạo bảng users với một cột sẽ lưu số điện thoại đã mã hóa:
-- Tạo bảng với cột để lưu dữ liệu đã mã hóa
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
phone_encrypted BYTEA -- Ở đây sẽ lưu số điện thoại đã mã hóa
);
Bước 2: Thêm dữ liệu với mã hóa
Bây giờ thêm một user, mã hóa số điện thoại của họ:
-- Thêm dữ liệu với mã hóa
INSERT INTO users (username, phone_encrypted)
VALUES ('john_doe', pgp_sym_encrypt('123-456-7890', 'my_secret_key'));
Lưu ý khi dùng hàm pgp_sym_encrypt. my_secret_key — đây là key đối xứng của mình. Ngoài đời thì key phải phức tạp và được bảo vệ kỹ càng nhé.
Bước 3: Lấy dữ liệu với giải mã
Khi cần truy cập dữ liệu, mình có thể giải mã nó như sau:
-- Lấy dữ liệu với giải mã
SELECT
username,
pgp_sym_decrypt(phone_encrypted, 'my_secret_key') AS phone
FROM users;
Nếu key đúng, bạn sẽ thấy số điện thoại gốc.
Nâng cao: Thêm mã hóa vào bảng đã có
Giả sử bảng đã tồn tại và bạn muốn bắt đầu mã hóa dữ liệu ở một cột nào đó? Xem ví dụ này nhé.
Bước 1: Thêm cột mới
Giả sử có bảng customers, và bạn muốn mã hóa cột số thẻ tín dụng:
-- Thêm cột mới để lưu dữ liệu đã mã hóa
ALTER TABLE customers ADD COLUMN card_number_encrypted BYTEA;
Bước 2: Chuyển dữ liệu sang cột mã hóa
Mã hóa dữ liệu hiện có và chuyển sang cột mới:
-- Mã hóa dữ liệu và chuyển sang cột mới
UPDATE customers
SET card_number_encrypted = pgp_sym_encrypt(card_number, 'my_other_secret_key');
Bước 3: Xóa cột chưa mã hóa
Sau khi mã hóa thành công, có thể xóa cột cũ đi:
-- Xóa cột cũ chưa mã hóa
ALTER TABLE customers DROP COLUMN card_number;
Bây giờ dữ liệu đã được bảo vệ bằng mã hóa, chỉ ai có key mới truy cập được.
Lưu ý khi làm việc với dữ liệu đã mã hóa
Có vài điểm quan trọng khi làm việc với các cột đã mã hóa:
Kiểu dữ liệu:
- Giá trị đã mã hóa được lưu ở dạng nhị phân (
BYTEA), không phải dạng đọc được. - Khi truy vấn cần dùng hàm giải mã.
Tìm kiếm và lọc:
- Không thể tìm trực tiếp theo dữ liệu đã mã hóa, ví dụ:
SELECT * FROM users WHERE phone_encrypted = '123-456-7890'; -- KHÔNG HOẠT ĐỘNG!
- Thay vào đó, có thể giải mã dữ liệu để lọc:
SELECT *
FROM users
WHERE pgp_sym_decrypt(phone_encrypted, 'my_secret_key') = '123-456-7890';
Hiệu năng:
Mã hóa và giải mã có thể làm chậm truy vấn. Chỉ dùng khi thực sự cần thiết thôi nhé.
Tình huống thực tế: bảo vệ mật khẩu
Lưu mật khẩu là một trong những nhiệm vụ mã hóa phổ biến nhất. Đừng bao giờ lưu mật khẩu dạng plain text (ý tưởng tệ), mà nên hash nó.
Hash mật khẩu bằng pgcrypto
Mình sẽ dùng hàm crypt() để hash mật khẩu an toàn:
-- Hash mật khẩu khi thêm bản ghi
INSERT INTO users (username, phone_encrypted)
VALUES ('alice', crypt('my_secure_password', gen_salt('bf')));
Ở đây gen_salt('bf') tạo salt để hash mật khẩu.
Để kiểm tra mật khẩu, so sánh hash như sau:
-- So sánh mật khẩu đã hash
SELECT username
FROM users
WHERE crypt('my_secure_password', phone_encrypted) = phone_encrypted;
Mẹo bảo mật
- Lưu key riêng biệt:
Đừng bao giờ lưu key đối xứng cùng database với dữ liệu đã mã hóa.
- Dùng key phức tạp:
Key đơn giản kiểu "123" dễ bị đoán lắm.
- Thường xuyên đổi key:
Để tránh lộ dữ liệu, nên định kỳ đổi key và mã hóa lại dữ liệu.
GO TO FULL VERSION