数据库的Hibernate加密对应用程序完全透明

Azd*_*der 8 mysql database encryption grails hibernate

我正在研究Grails 1.0.4项目,该项目必须在不到两周的时间内发布,而客户只是要求数据库中的所有数据都应加密.

由于应用程序本身的每个数据库访问的加密可能需要花费大量时间并且容易出错,因此我寻求的解决方案是某种对应用程序透明的加密.

有没有办法设置Hibernate加密所有表中的所有数据(除了id和版本列可能)或者我应该寻求MySQL解决方案(我们使用的是MySQL 5.0)?

编辑:感谢您提供替代解决方案的所有帖子,如果客户改变了想法,那就太棒了.至于现在,要求是"数据库中没有纯文本".

我要指出的第二件事是我正在使用Grails,对于那些不熟悉Grails的人来说,这是一个关于配置的约定,所以即使应用程序中的小变化也应该避免.

Bur*_*ith 5

如果您在应用程序中完成工作,则可以使用Hibernate自定义类型,并且不会对代码添加许多更改.

这是我使用的加密字符串自定义类型:

import org.hibernate.usertype.UserType
import org.apache.log4j.Logger

import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.sql.Types

class EncryptedString implements UserType {

  // prefix category name with 'org.hibernate.type' to make logging of all types easier
  private final Logger _log = Logger.getLogger('org.hibernate.type.com.yourcompany.EncryptedString')

  Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
    String value = rs.getString(names[0])

    if (!value) {
      _log.trace "returning null as column: $names[0]"
      return null
    }

    _log.trace "returning '$value' as column: $names[0]"
    return CryptoUtils.decrypt(value)
  }

  void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
    if (value) {
      String encrypted = CryptoUtils.encrypt(value.toString())
      _log.trace "binding '$encrypted' to parameter: $index"
      st.setString index, encrypted
    }
    else {
      _log.trace "binding null to parameter: $index"
      st.setNull(index, Types.VARCHAR)
    }
  }

  Class<String> returnedClass() { String }

  int[] sqlTypes() { [Types.VARCHAR] as int[] }

  Object assemble(Serializable cached, Object owner) { cached.toString() }

  Object deepCopy(Object value) { value.toString() }

  Serializable disassemble(Object value) { value.toString() }

  boolean equals(Object x, Object y) { x == y }

  int hashCode(Object x) { x.hashCode() }

  boolean isMutable() { true }

  Object replace(Object original, Object target, Object owner) { original }
}
Run Code Online (Sandbox Code Playgroud)

基于此,为int,long等创建类似的类应该很简单.要使用它,请将类型添加到映射闭包:

class MyDomainClass {

  String name
  String otherField

  static mapping = {
    name type: EncryptedString
    otherField type: EncryptedString
  }
}
Run Code Online (Sandbox Code Playgroud)

我省略了CryptoUtils.encrypt()和CryptoUtils.decrypt()方法,因为那不是特定于Grails的.我们使用的是AES,例如"Cipher cipher = Cipher.getInstance('AES/CBC/PKCS5Padding')".无论你最终使用什么,确保它是一个双向加密,即不使用SHA-256.


Azd*_*der 2

好吧,我已经很久没有问这个问题了。同时,感谢您的所有回答。他们在处理加密整个数据库的原始想法时非常棒,但要求更改为仅加密敏感的用户信息,例如姓名和地址。所以解决方案就像下面的代码一样。

我们实现了一个加密器,它从记录中读取加密方法(因此每个记录可以有不同的加密),并使用它将临时重复字段连接到数据库中加密的字段。额外的好处/缺点是:

  • 数据也在内存中加密,因此每次访问 getFirstName 方法都会解密数据(我猜有一种方法可以缓存解密的数据,但在本例中我不需要它)
  • 加密字段不能与默认的 grails/hibernate 方法一起使用来搜索数据库,我们在服务中创建了自定义方法来获取数据,对其进行加密,然后在查询的 where 子句中使用加密的数据。使用 User.withCriteria 很容易

    类用户{

    byte[] encryptedFirstName
    byte[] encryptedLastName
    byte[] encryptedAddress
    
    Date dateCreated // automatically set date/time when created
    Date lastUpdated // automatically set date/time when last updated
    
    EncryptionMethod encryptionMethod = ConfigurationHolder.config.encryption.method
    
    def encrypter = Util.encrypter
    
    static transients = [ 
    'firstName', 
    'lastName', 
    'address',
    'encrypter'
    ]
    
    static final Integer BLOB_SIZE = 1024
    
    static constraints = {
    
        encryptedFirstName maxSize: BLOB_SIZE, nullable: false
        encryptedLastName maxSize: BLOB_SIZE, nullable: false
    
        encryptedAddress maxSize: BLOB_SIZE, nullable: true
    
        encryptionMethod nullable: false
    
    } // constraints
    
    String getFirstName(){
        decrypt('encryptedFirstName')
    }
    
    void setFirstName(String item){     
        encrypt('encryptedFirstName',item)
    }
    
    String getLastName(){
        decrypt('encryptedLastName')
    }
    
    void setLastName(String item){
        encrypt('encryptedLastName',item)       
    }
    
    String getAddress(){
        decrypt('encryptedAddress')
    }
    
    void setAddress(String item){
        encrypt('encryptedAddress',item)        
    }
    
    byte[] encrypt(String name, String value) {
    
        if( null == value ) {
            log.debug "null string to encrypt for '$name', returning null"
            this.@"$name" = null
            return
        }
    
        def bytes = value.getBytes(encrypter.ENCODING_CHARSET)
        def method = getEncryptionMethod()
    
    
        byte[] res 
    
        try {
            res = encrypter.encrypt( bytes, method )            
        } catch(e) {
            log.warn "Problem encrypting '$name' data: '$string'", e
        }
    
        log.trace "Encrypting '$name' with '$method' -> '${res?.size()}' bytes"
    
        this.@"$name" = res
    
    }
    
    String decrypt(String name) {
    
        if(null == this.@"$name") {
            log.debug "null bytes to decrypt for '$name', returning null"
            return null
        }
    
        def res 
        def method = getEncryptionMethod()
    
        try {
            res = new String(encrypter.decrypt(this.@"$name", method), encrypter.ENCODING_CHARSET )
        } catch(e) {
            log.error "Problem decrypting '$name'", e
        }
    
        log.trace "Decrypting '$name' with '$method' -> '${res?.size()}' bytes"
    
        return res
    }
    
    Run Code Online (Sandbox Code Playgroud)

    }