0

Apache Derby Bài 36: Mã hóa cơ sở dữ liệu ở cấp độ đĩa (Disk-Level Encryption)

1. Tại sao cần mã hóa ở cấp độ đĩa?

Ở Bài 35, chúng ta đã sao lưu cơ sở dữ liệu an toàn. Tuy nhiên, hãy tưởng tượng một kịch bản tồi tệ: Máy chủ bị tin tặc xâm nhập và copy toàn bộ thư mục CSDL, hoặc kẻ gian đột nhập phòng máy đánh cắp ổ cứng vật lý.

Lúc này, dù hệ thống của bạn có tường lửa hay mật khẩu đăng nhập mạnh đến đâu cũng trở nên vô nghĩa. Kẻ gian chỉ cần lấy thư mục đó, gắn vào một máy tính khác, bật Apache Derby lên và đọc toàn bộ dữ liệu (Bảo mật thông tin khách hàng, thẻ tín dụng, doanh thu).

Để chống lại lỗ hổng vật lý này, cơ sở dữ liệu phải bảo vệ Dữ liệu ở trạng thái nghỉ (Data at Rest). Apache Derby cung cấp tính năng Disk-Level Encryption (Mã hóa cấp độ đĩa). Khi được bật, toàn bộ các khối dữ liệu ghi xuống tệp (trong thư mục seg0/) sẽ bị xáo trộn thành các chuỗi byte vô nghĩa. Chỉ khi cung cấp đúng "Chìa khóa" (Boot Password) lúc khởi động, Derby Engine mới có thể giải mã và đưa dữ liệu lên RAM để đọc hiểu.

2. Nền tảng mã hóa của Apache Derby

Derby không tự phát minh ra thuật toán mã hóa riêng mà tái sử dụng JCE (Java Cryptography Extension) được tích hợp sẵn trong máy ảo Java (JVM).

  • Thuật toán mặc định: Derby thường sử dụng DES hoặc AES (phụ thuộc vào cấu hình JVM của bạn). Trong môi trường hiện đại, AES (Advanced Encryption Standard) với độ dài khóa 128-bit hoặc 256-bit là chuẩn mực.

  • Mật khẩu khởi động (Boot Password): Là chìa khóa gốc dùng để mã hóa khóa dữ liệu. Trong Derby, mật khẩu này bắt buộc phải có độ dài tối thiểu 8 ký tự.

3. Khởi tạo một CSDL mã hóa hoàn toàn mới

Để tạo một CSDL được mã hóa, bạn không cần dùng câu lệnh SQL nào cả. Mọi cấu hình đều được chỉ định ngay tại chuỗi kết nối (JDBC URL) ở lần đầu tiên tạo CSDL.

Bạn cần truyền thêm 2 thuộc tính:

  1. dataEncryption=true: Bật tính năng mã hóa đĩa.

  2. bootPassword=Mật_khẩu_của_bạn: Cung cấp chìa khóa mã hóa (tối thiểu 8 ký tự).

Ví dụ:

Plaintext

jdbc:derby:my_secure_db;create=true;dataEncryption=true;bootPassword=MySuperSecretKey123!

4. Kết nối và vận hành CSDL đã mã hóa

Sau khi CSDL được tạo thành công, các tệp vật lý trên ổ cứng đã bị mã hóa. Ở những lần chạy tiếp theo (khi ứng dụng khởi động lại), bạn bắt buộc phải truyền lại bootPassword để Derby có thể mở khóa tệp.

Nếu bạn quên truyền bootPassword, hoặc truyền sai mật khẩu, Derby sẽ ném ra ngoại lệ SQLException với mã lỗi XJ040 (Bị khóa) hoặc XJ049 (Lỗi giải mã/Sai mật khẩu), và từ chối kết nối.

Mã nguồn Java minh họa:

Java

package com.hieu.derby.security;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class DerbyEncryptionDemo {

    // URL Kết nối thông thường (Không có create=true, chỉ dùng để kết nối)
    private static final String SECURE_DB_URL = 
            "jdbc:derby:my_secure_db;bootPassword=MySuperSecretKey123!";

    public static void main(String[] args) {
        
        System.out.println("Đang mở khóa và kết nối CSDL mã hóa...");

        try (Connection conn = DriverManager.getConnection(SECURE_DB_URL)) {
            System.out.println("=> Mở khóa thành công! Dữ liệu đã sẵn sàng.");
            
            // Từ đây trở đi, mọi thao tác SQL (SELECT, INSERT) diễn ra HOÀN TOÀN BÌNH THƯỜNG
            // Derby sẽ tự động giải mã khi đọc và mã hóa khi ghi xuống đĩa một cách trong suốt (transparent).
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("CREATE TABLE tai_khoan_ngan_hang (so_tk VARCHAR(20), so_du DECIMAL(15,2))");
                stmt.execute("INSERT INTO tai_khoan_ngan_hang VALUES ('123456789', 5000000.00)");
                System.out.println("=> Đã chèn dữ liệu nhạy cảm an toàn vào ổ đĩa.");
            }

        } catch (SQLException e) {
            System.err.println("=> LỖI MỞ KHÓA: " + e.getSQLState() + " - " + e.getMessage());
        }
    }
}

5. Mã hóa một CSDL cũ chưa được mã hóa

Nếu bạn đang có một CSDL cũ (chưa được mã hóa) và muốn nâng cấp bảo mật cho nó, Derby hỗ trợ thực hiện việc này ngay trong lúc khởi động CSDL.

Bạn chỉ cần kết nối tới CSDL cũ đó với thuộc tính dataEncryption=true và cung cấp một mật khẩu mới.

Chuỗi kết nối để mã hóa CSDL cũ:

Plaintext

jdbc:derby:my_old_db;dataEncryption=true;bootPassword=NewStrongPassword2026

Khi Derby nhận được lệnh này, nó sẽ quét qua toàn bộ các khối dữ liệu cũ, mã hóa chúng và ghi đè lại xuống đĩa. Quá trình này có thể mất vài phút tùy thuộc vào dung lượng dữ liệu của bạn. Sau khi hoàn tất, CSDL đó chính thức trở thành CSDL mã hóa.

6. Lời khuyên và Cảnh báo bảo mật

  1. Đánh đổi hiệu năng (Performance Penalty): Mã hóa đĩa không miễn phí. Mỗi lệnh đọc/ghi đều bắt CPU phải thực hiện các thuật toán mã hóa/giải mã. Hiệu năng hệ thống (đặc biệt là tác vụ INSERT hàng loạt) có thể giảm từ 10% đến 30%. Chỉ bật mã hóa khi thực sự lưu trữ dữ liệu nhạy cảm (Tài chính, Y tế, Mật khẩu).

  2. Quản lý Boot Password: Trong mã nguồn Java thực tế, TUYỆT ĐỐI KHÔNG hardcode (viết cứng) mật khẩu bootPassword vào file .java như ví dụ trên. Hãy đưa mật khẩu vào biến môi trường hệ điều hành (Environment Variables) hoặc một trình quản lý bảo mật (như HashiCorp Vault), và Java sẽ đọc lên lúc Runtime.

  3. Mất chìa khóa = Mất dữ liệu: Không có bất kỳ "cửa sau" (backdoor) hay công cụ khôi phục nào nếu bạn quên bootPassword. Dữ liệu sẽ biến thành một khối byte vô nghĩa vĩnh viễn.

Mã hóa cấp độ đĩa giúp bảo vệ dữ liệu trước những kẻ tấn công vật lý bên ngoài. Nhưng đối với các nhân viên bên trong hệ thống (như lập trình viên, kế toán, nhân viên sale), chúng ta cần một cơ chế để kiểm soát ai được phép xem bảng nào. Bài tiếp theo sẽ giải quyết bài toán này: Apache Derby Bài 37: Xác thực người dùng (Authentication) và Phân quyền truy cập (Authorization).


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.