在 Hibernate 中加密特定列:如何处理现有数据以及如何正确实现 @ColumnTransformer?

mix*_*tou 5 java mysql sql hibernate

我已经使用 MySQL 数据库构建了一个 Web 应用程序,其中包含患者数据。根据GDPR,患者姓名必须在数据库内加密。为了连接和执行对数据库的操作,我使用 Hibernate 5。

在网上搜索,我发现了很多关于如何加密 db 表中的一个或多个特定列的信息。主要有以下三种做法:

  • 使用Hibernate 的@ColumnTransformer注解,它对现有代码的破坏性最小,需要编写的代码最少
  • 使用Jasypt及其 Hibernate 集成,这对现有代码更具破坏性,并且需要几行代码。
  • 实现一个需要编写相当多行的 JPA 属性转换器

我决定使用@ColumnTransformer这似乎是最简单的实现。如果您认为其他方法之一更好,请说出来并解释原因。

然而,我的问题与现有数据有关。我的数据库已经有未加密的数据,必须对其进行加密才能与@ColumnTransformer实现一起使用。我打算使用以下注释:

@ColumnTransformer(
    read = "pgp_sym_decrypt(lastName, 'mySecretKey')", 
    write = "pgp_sym_encrypt(?, 'mySecretKey')"
)
Run Code Online (Sandbox Code Playgroud)

@ColumnTransformer(
    read = "pgp_sym_decrypt(firstName, 'mySecretKey')", 
    write = "pgp_sym_encrypt(?, 'mySecretKey')"
)
Run Code Online (Sandbox Code Playgroud)

到相应的列。

我应该如何加密现有数据以符合上述注释?我应该使用什么 SQL 代码?

mix*_*tou 6

MySQL 支持以下功能:

  • AES_ENCRYPT(str, key_str);
  • AES_DECRYPT(crypt_str,key_str);

但是,我无法使用以下内容更新所有 MySQL 条目(因为 aes_encrypt 返回二进制文件):

UPDATE Patient SET firstName=AES_ENCRYPT(firstName, "mySecretKey"), lastName=AES_ENCRYPT(lastName, "mySecretKey") //NOT WORKING
Run Code Online (Sandbox Code Playgroud)

解决办法是:

  1. 使用 MySQL 命令重命名现有列:

    ALTER TABLE Patient CHANGE firstName firstName-old;
    
    ALTER TABLE Patient CHANGE lastName lastName-old;
    
    Run Code Online (Sandbox Code Playgroud)
  2. varbinary(512)使用命令创建两个新的 MySQL 类型列:

    ALTER TABLE Patient ADD COLUMN lastName VARBINARY(512) NOT NULL;
    
    ALTER TABLE Patient ADD COLUMN firstName VARBINARY(512) NOT NULL;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用以下命令更新旧列中的新列:

    UPDATE `gourvas_platform`.`Patient` SET firstName=aes_encrypt(`firstName-old`, "my secret"), lastName=aes_encrypt(`lastName-old`, "mysecret");
    
    Run Code Online (Sandbox Code Playgroud)
  4. 现在我们可以安全地删除旧列

  5. 最后使用以下 Hibernate@ColumnTransformer注释:

    @ColumnTransformer(
        read = "AES_DECRYPT(lastName, 'mySecretKey')", 
        write = "AES_ENCRYPT(?, 'mySecretKey')"
    )  
    
    Run Code Online (Sandbox Code Playgroud)

    @ColumnTransformer(
        read = "AES_DECRYPT(firstName, 'mySecretKey')", 
        write = "AES_ENCRYPT(?, 'mySecretKey')"
    )  
    
    Run Code Online (Sandbox Code Playgroud)

注意:因为我使用的是 MySQL 5.7 并且 AES_DECRYPT 函数返回binary[]而不是String,所以我需要发cast短信。所以上面的@ColumnTransformer需要改成下面的:

@ColumnTransformer(
read = "cast(aes_decrypt(lastName, 'my secret') as char(255))", 
write = "aes_encrypt(?, 'mysecret')"
) 
Run Code Online (Sandbox Code Playgroud)

@ColumnTransformer(
read = "cast(aes_decrypt(firstName, 'myscret') as char(255))", 
write = "aes_encrypt(?, 'mysecret')"
) 
Run Code Online (Sandbox Code Playgroud)