Tư duy Feynman • Trực quan hình họa • Java / Spring Boot

Chương 16: Database Security & Hardening

Bảo vệ toàn diện tầng dữ liệu: Xác thực IAM Database, phân quyền RBAC tối thiểu, chuẩn hóa PreparedStatement, mã hóa TDE tĩnh/động và kiểm toán pgAudit.

#01 Database Authentication (Xác thực Cơ sở Dữ liệu)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Cơ sở dữ liệu là hầm chứa két sắt của ngân hàng. Nếu nhân viên chỉ nói 'Tôi là nhân viên' mà không có vân tay và thẻ từ (xác thực yếu hoặc mật khẩu mặc định), bất kỳ ai mặc đồng phục cũng vào vơ vét được tiền. Xác thực CSDL chuẩn yêu cầu chứng chỉ số TLS, tài khoản riêng biệt, hoặc IAM Role động với hạn dùng ngắn.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Database Authentication (Xác thực Cơ sở Dữ liệu)
Spring App IAM DB Token HikariCP Pool Kết nối TLS Port 5432 PostgreSQL Xác thực danh tính DB Session Phiên an toàn 15 phút 🛡️ Xác thực CSDL an toàn: IAM Database Auth & TLS Connection
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: user: sa, pass: adminuser: root, pass: emptyuser: app_service, auth: aws_iam_token
⚠️ Xác thực yếu với tài khoản root/sa dùng chung cho mọi dịch vụ!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THẢM HỌA: Dùng tài khoản 'root' hoặc 'postgres' với mật khẩu đơn giản
spring.datasource.url=jdbc:postgresql://db.prod:5432/main_db
spring.datasource.username=postgres
spring.datasource.password=postgres123
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Dùng AWS IAM Database Authentication hoặc Spring Cloud Vault
@Configuration
public class HikariIamDataSourceConfig {
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://db.prod:5432/main_db?sslmode=verify-full");
        config.setUsername("app_iam_user");
        // Tự động sinh IAM Auth Token có thời hạn 15 phút
        config.setPassword(RdsIamTokenGenerator.generateAuthToken());
        return new HikariDataSource(config);
    }
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Tại sao nên dùng IAM Database Authentication thay vì mật khẩu tĩnh?
A. Vì IAM token tự hết hạn sau 15 phút và không cần lưu mật khẩu vào file cấu hình hay mã nguồn.
B. Vì CSDL chạy nhanh hơn gấp 10 lần.
C. Vì không cần cài đặt cơ sở dữ liệu.
D. Vì SQL Injection sẽ tự động biến mất.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#02 Database Authorization & RBAC (Phân quyền CSDL theo vai trò)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Trong một khách sạn, khách thuê phòng chỉ mở được cửa phòng mình, lao công mở được các phòng dọn dẹp, chỉ quản trị viên mới vào được phòng tài vụ. Nếu bạn cấp thẻ vạn năng (quản trị CSDL) cho người phục vụ bàn (ứng dụng đọc sản phẩm), họ có thể lỡ tay hoặc bị ép xóa sạch sổ sách khách sạn.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Database Authorization & RBAC (Phân quyền CSDL theo vai trò)
App Role app_readonly PostgreSQL RBAC GRANT SELECT ON tbl Tác vụ ghi DROP TABLE / INSERT Kết quả Permission Denied 🛡️ RBAC trong CSDL: Chỉ cấp quyền SELECT cho các dịch vụ truy vấn thông tin
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: ROLE: app_read, QUERY: SELECT * FROM productsROLE: app_read, QUERY: DROP TABLE usersROLE: app_admin, QUERY: ALTER TABLE
⚠️ Ứng dụng kết nối CSDL với toàn quyền DDL/DML (SUPERUSER)!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
-- NGUY HIỂM: Cấp quyền sở hữu toàn bộ bảng hoặc SUPERUSER cho tài khoản ứng dụng
CREATE USER web_user WITH PASSWORD 'secret';
ALTER USER web_user WITH SUPERUSER; -- CỰC KỲ NGUY HIỂM!
✅ Secure Java Code (Chuẩn bảo mật)
-- CHUẨN AN TOÀN: Tạo Role riêng biệt và chỉ GRANT quyền tối thiểu cần thiết
CREATE ROLE app_readonly_role;
GRANT CONNECT ON DATABASE main_db TO app_readonly_role;
GRANT USAGE ON SCHEMA public TO app_readonly_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly_role;

CREATE USER catalog_service WITH PASSWORD 'StrongR4nd0mP@ss!';
GRANT app_readonly_role TO catalog_service;
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Quyền nào dưới đây TUYỆT ĐỐI KHÔNG NÊN cấp cho tài khoản CSDL của ứng dụng Web thông thường?
A. DROP TABLE, ALTER TABLE, TRUNCATE (các quyền DDL thay đổi cấu trúc bảng).
B. SELECT.
C. INSERT vào bảng logs.
D. CONNECT vào database.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#03 Principle of Least Privilege in DB (Nguyên tắc Đặc quyền Tối thiểu)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Khi thuê thợ sơn nhà, bạn chỉ giao chìa khóa mở cổng và sân trước, chứ không bao giờ đưa chìa khóa két sắt hay phòng ngủ chính. Tài khoản CSDL của microservice chỉ được cấp đúng những bảng và thao tác mà service đó cần để hoạt động.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Principle of Least Privilege in DB (Nguyên tắc Đặc quyền Tối thiểu)
Payment Service Truy cập DB Least Privilege Chỉ bảng payments Đọc bảng user_passwords Truy cập trái phép CSDL từ chối 403 Access Denied 🛡️ Least Privilege: Mỗi Service chỉ truy cập đúng Schema/Table của mình
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: SERVICE: notification, ACCESS: email_queueSERVICE: notification, ACCESS: credit_cards
⚠️ Service có thể đọc toàn bộ CSDL bao gồm cả thông tin thẻ tín dụng!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// TẤT CẢ SERVICE DÙNG CHUNG 1 TÀI KHOẢN DB DUY NHẤT CÓ QUYỀN TOÀN CỤC
spring.datasource.username=db_global_user
✅ Secure Java Code (Chuẩn bảo mật)
// MỖI MICROSERVICE DÙNG TÀI KHOẢN RIÊNG VỚI SCHEMA ĐỘC LẬP
// Auth Service: chỉ truy cập bảng users, refresh_tokens
// Payment Service: chỉ truy cập bảng transactions, payment_methods
// Reporting Service: chỉ được cấp quyền SELECT trên bản sao Read-Replica
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Hệ quả nguy hiểm nhất khi vi phạm nguyên tắc Least Privilege trong CSDL là gì?
A. Khi 1 microservice phụ (như gửi email) bị dính SQL Injection, hacker có thể chiếm quyền đọc/ghi toàn bộ CSDL tài chính.
B. Server chạy tốn điện hơn.
C. Mạng WiFi bị ngắt kết nối.
D. File JAR tăng kích thước.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#04 PreparedStatement & Parameterized Queries (Truy vấn tham số hóa)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Hãy tưởng tượng bạn đưa một bức thư mẫu đã được in sẵn khung chữ, chỉ có một ô trống nhỏ để người dùng điền tên bằng bút mực. Dù họ có cố viết vào đó câu lệnh 'HÃY ĐỐT NHÀ ĐI', thì hệ thống vẫn coi đó chỉ là cái tên của một người, chứ không bao giờ thi hành lệnh đốt nhà.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): PreparedStatement & Parameterized Queries (Truy vấn tham số hóa)
Client Param ' OR '1'='1 PreparedStatement Tách riêng mã lệnh & tham số Database Engine Chỉ coi là chuỗi Literal thô Kết quả Không bị SQL Injection 🛡️ PreparedStatement: Tách rời hoàn toàn Logic SQL và Dữ liệu đầu vào
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: admin' OR 1=1 --standard_user1; DROP TABLE users;
⚠️ Nguy cơ SQLi nếu ghép chuỗi trực tiếp vào câu lệnh SQL!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THẢM HỌA: Nối chuỗi SQL thô trong Java
public User findUser(String username) throws SQLException {
    String sql = "SELECT * FROM users WHERE username = '" + username + "'"; // SQL INJECTION!
    Statement stmt = connection.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
    return mapUser(rs);
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Dùng PreparedStatement hoặc Spring Data JPA Parameterized Query
public User findUser(String username) throws SQLException {
    String sql = "SELECT * FROM users WHERE username = ?";
    try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
        pstmt.setString(1, username); // Dữ liệu được escape an toàn ở tầng driver
        try (ResultSet rs = pstmt.executeQuery()) {
            return mapUser(rs);
        }
    }
}
// Hoặc trong Spring Data JPA:
@Query("SELECT u FROM User u WHERE u.username = :username")
Optional findByUsername(@Param("username") String username);
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Tại sao PreparedStatement có thể triệt tiêu hoàn toàn SQL Injection?
A. Vì CSDL biên dịch cây cú pháp SQL (AST) trước, sau đó mới gắn tham số vào dưới dạng dữ liệu thô (Literal), không làm biến đổi cấu trúc lệnh.
B. Vì nó tự động xóa bỏ các ký tự dấu nháy đơn.
C. Vì nó mã hóa CSDL bằng mật khẩu 128-bit.
D. Vì nó chỉ chạy trên hệ điều hành Linux.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#05 Stored Procedure Security & Dynamic SQL (Bảo mật Thủ tục lưu trữ)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Thủ tục lưu trữ (Stored Procedure) giống như một cái máy bán hàng tự động: bạn ấn nút A1, máy nhả ra lon coca. Nhưng nếu kỹ sư chế tạo chiếc máy đó lại lắp một cánh cửa mở toang bên sườn để ai muốn lấy gì thì tự thò tay vào bốc (dùng Dynamic SQL EXECUTE trong Procedure), thì chiếc máy mất hoàn toàn ý nghĩa bảo vệ.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Stored Procedure Security & Dynamic SQL (Bảo mật Thủ tục lưu trữ)
SP Call sp_get_user(in_param) Dynamic SQL EXEC Ghép chuỗi EXECUTE sql Stored Proc An Toàn Dùng tham số sp_executesql Thực thi DB Kiểm soát an toàn ⚠️ Stored Procedure chỉ an toàn khi KHÔNG dùng Dynamic SQL ghép chuỗi
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: p_user_id: 105; TRUNCATE TABLE logs;p_user_id: 105p_name: '' OR 1=1
⚠️ Stored Procedure nếu dùng Dynamic SQL ghép chuỗi vẫn bị SQL Injection như thường!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
-- NGUY HIỂM: Stored Procedure ghép chuỗi động (Dynamic SQL Injection)
CREATE PROCEDURE FindUserDynamic(IN p_username VARCHAR(100))
BEGIN
    SET @sql = CONCAT('SELECT * FROM users WHERE username = '', p_username, ''');
    PREPARE stmt FROM @sql;
    EXECUTE stmt; -- LỖ HỔNG SQL INJECTION NẰM TRONG DB!
    DEALLOCATE PREPARE stmt;
END;
✅ Secure Java Code (Chuẩn bảo mật)
-- CHUẨN AN TOÀN: Stored Procedure tĩnh có tham số hoặc sp_executesql
CREATE PROCEDURE FindUserSafe(IN p_username VARCHAR(100))
BEGIN
    SELECT * FROM users WHERE username = p_username; -- Biên dịch tĩnh an toàn 100%
END;

-- Gọi an toàn từ Java Spring Boot:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(dataSource)
    .withProcedureName("FindUserSafe");
Map out = jdbcCall.execute(new MapSqlParameterSource("p_username", username));
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Quan niệm: 'Chỉ cần chuyển hết truy vấn vào Stored Procedure là hệ thống an toàn tuyệt đối khỏi SQL Injection' là đúng hay sai?
A. SAI, vì nếu bên trong Stored Procedure sử dụng Dynamic SQL (ghép chuỗi EXECUTE/sp_executesql không tham số), lỗ hổng SQL Injection vẫn tồn tại.
B. ĐÚNG, Stored Procedure luôn luôn chặn mọi cuộc tấn công.
C. ĐÚNG, vì CSDL không cho phép chứa dấu nháy đơn.
D. SAI, vì Stored Procedure chỉ chạy được trên Oracle.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#06 Encryption at Rest — TDE (Mã hóa CSDL khi lưu trữ tĩnh)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Nếu trộm đột nhập vào kho dữ liệu và khiêng trọn ổ cứng máy chủ về nhà cắm vào máy của chúng, chúng có đọc được dữ liệu không? Nếu có TDE (Transparent Data Encryption), toàn bộ ổ đĩa chỉ là một mớ rác vô nghĩa trừ khi chúng có chìa khóa giải mã nằm trong chip bảo mật HSM phần cứng riêng biệt.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Encryption at Rest — TDE (Mã hóa CSDL khi lưu trữ tĩnh)
Database App Ghi bản ghi TDE Engine Mã hóa AES-256 Physical Disk Lưu trữ Block mã hóa Kẻ trộm ổ cứng Chỉ thấy chuỗi rác 🛡️ TDE: Bảo vệ dữ liệu ngay cả khi ổ đĩa vật lý hoặc bản backup bị đánh cắp
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: STORAGE_STATE: unencrypted_raw_diskSTORAGE_STATE: aws_kms_ebs_encryptedSTORAGE_STATE: postgresql_tde_active
⚠️ Ổ cứng lưu trữ CSDL chưa được mã hóa tĩnh!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
# NGUY HIỂM: Tạo ổ đĩa lưu trữ CSDL không mã hóa trên Đám mây
resource "aws_ebs_volume" "db_storage" {
  availability_zone = "us-east-1a"
  size              = 100
  encrypted         = false # RỦI RO VI PHẠM PCI-DSS / HIPAA!
}
✅ Secure Java Code (Chuẩn bảo mật)
# CHUẨN AN TOÀN: Bắt buộc mã hóa ổ cứng và CSDL với AWS KMS
resource "aws_db_instance" "prod_db" {
  allocated_storage     = 100
  engine                = "postgres"
  instance_class        = "db.r6g.xlarge"
  storage_encrypted     = true
  kms_key_id            = aws_kms_key.db_encryption_key.arn # Quản lý khóa tập trung
}

// Trong Java Spring Data: Mã hóa cột nhạy cảm ở tầng ứng dụng (JPA AttributeConverter)
@Convert(converter = EncryptedStringConverter.class)
private String ssn;
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Encryption at Rest (TDE) bảo vệ hệ thống trước tình huống nguy cơ nào sau đây?
A. Bảo vệ dữ liệu khi ổ đĩa vật lý, bản backup CSDL hoặc file snapshot bị sao chép trộm ngoài ý muốn.
B. Chống lại tấn công SQL Injection từ giao diện Web.
C. Chống tấn công XSS trên trình duyệt người dùng.
D. Tăng tốc độ truy vấn SELECT JOIN.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#07 Encryption in Transit (Mã hóa đường truyền CSDL với SSL/TLS)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn gọi điện thoại nói mật khẩu cho bạn mình. Nếu đường dây điện thoại bị kẻ khác kẹp thiết bị nghe lén (Man-in-the-Middle trên mạng nội bộ LAN/Cloud), chúng nghe rõ từng chữ bạn nói. Bật TLS trên kết nối CSDL giống như việc hai người nói chuyện bằng ngôn ngữ mật mã mà chỉ có 2 người hiểu.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Encryption in Transit (Mã hóa đường truyền CSDL với SSL/TLS)
Spring Boot HikariCP JDBC TLS 1.3 Tunnel Mã hóa gói tin mạng Wireshark Sniffer Không đọc được dữ liệu PostgreSQL Server Giải mã an toàn 🛡️ Encryption in Transit: sslmode=verify-full bảo vệ kết nối JDBC
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: sslmode=disablesslmode=requiresslmode=verify-full
⚠️ sslmode=disable: Gói tin SQL và thông tin đăng nhập truyền đi dạng văn bản rõ trên mạng!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// NGUY HIỂM: Kết nối JDBC không mã hóa hoặc tắt kiểm tra SSL
spring.datasource.url=jdbc:postgresql://db.company.internal:5432/finance?sslmode=disable
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Bắt buộc SSL với sslmode=verify-full và Truststore CA
spring.datasource.url=jdbc:postgresql://db.company.internal:5432/finance?ssl=true&sslmode=verify-full&sslrootcert=/etc/ssl/certs/rds-ca.pem

// Cấu hình HikariCP nâng cao:
hikari.data-source-properties.ssl=true
hikari.data-source-properties.sslmode=verify-full
hikari.data-source-properties.sslrootcert=/etc/ssl/certs/rds-ca.pem
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Trong kết nối JDBC PostgreSQL, mức độ sslmode nào là an toàn nhất chống MITM?
A. sslmode=verify-full (vừa mã hóa đường truyền vừa xác thực Hostname và Chứng chỉ số của Server).
B. sslmode=disable.
C. sslmode=allow.
D. sslmode=prefer.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#08 Database Auditing & Query Logging (Kiểm toán CSDL & Ghi log truy vấn)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Trong bảo tàng lưu giữ báu vật, ở mỗi tủ kính đều có một camera an ninh quay 24/7 ghi lại chính xác: Ai đã mở tủ? Vào lúc mấy giờ mấy phút? Đã lấy món gì ra? Nếu không có camera kiểm toán, đến khi kho báu biến mất bạn hoàn toàn mù tịt không biết ai đã đánh cắp.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Database Auditing & Query Logging (Kiểm toán CSDL & Ghi log truy vấn)
SQL Query UPDATE balance = 0 pgAudit Plugin Ghi log Session/Object Audit Log File Lưu vết bất biến WORM SIEM System Cảnh báo thao tác nhạy cảm 🛡️ Database Auditing: Giám sát toàn bộ thao tác DML/DDL nhạy cảm
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: UPDATE accounts SET balance = 50000000 WHERE id = 1SELECT * FROM public_postsDROP TABLE audit_trail
⚠️ Thao tác sửa đổi số dư tài khoản không có dấu vết kiểm toán!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
-- NGUY HIỂM: CSDL không cấu hình ghi log hoạt động DDL và thay đổi dữ liệu
-- File postgresql.conf để mặc định không audit
✅ Secure Java Code (Chuẩn bảo mật)
-- CHUẨN AN TOÀN: Kích hoạt Extension pgAudit trong PostgreSQL
-- postgresql.conf:
shared_preload_libraries = 'pgaudit'
pgaudit.log = 'write, ddl, role'
pgaudit.log_catalog = off
pgaudit.log_parameter = on -- Ghi cả tham số thực thi

-- Trong Spring Data JPA: Áp dụng Hibernate Envers lưu lịch sử thay đổi từng phiên bản
@Entity
@Audited // Tự động sinh bảng users_AUD lưu vết mọi lần INSERT/UPDATE/DELETE
public class User { ... }
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Tính năng chính của pgAudit hoặc Database Audit Logging là gì?
A. Cung cấp bản ghi kiểm toán chi tiết và đáng tin cậy về những ai đã thực hiện lệnh gì trên các bảng dữ liệu nhạy cảm để phục vụ điều tra và tuân thủ pháp lý.
B. Tự động sao lưu database sang USB.
C. Sửa lỗi chính tả trong câu lệnh SQL.
D. Thay thế tường lửa mạng.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)