Hibernate对数据库的加密对应用程序完全透明[英] Hibernate Encryption of Database Completely Transparent to Application

本文是小编为大家收集整理的关于Hibernate对数据库的加密对应用程序完全透明的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在研究一个必须在不到2周内发布的Grails 1.0.4项目,并且客户只是提出了一个数据库中的所有数据.

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

是否有一种设置Hibernate的方法来对所有表中的所有数据进行加密(Maybie the ID和版本列除外),或者我应该寻求mySQL解决方案(我们使用MySQL 5.0)?

编辑: 感谢您的所有帖子提供替代解决方案,如果客户改变了主意,那就太好了.目前,要求是"数据库中没有纯文本".

我想指出的第二件事是,我正在使用圣杯,因为那些与之不知所措的人,这是关于配置的约定,因此,即使对应用程序的少量更改也应避免.<<<<<<<<<<<<<<<<<<<

推荐答案

自从我问这个问题以来已经很长时间了.同时,感谢所有答案.在处理加密整个数据库的最初想法时,它们非常好,但是要求仅更改为加密敏感用户信息,例如名称和地址.因此,解决方案是下面的代码.

我们已经实现了一个加密程序,该加密机从记录中读取加密方法(因此每个记录可能会有不同的加密),并使用它将瞬态重复字段连接到数据库中加密的字段.附加的奖励/缺点是:

  • 数据也已在内存中加密,因此对方法getFirstName descrypts descrypts descrypts的每个访问(我想有一种方法可以缓存数据,但在这种情况下我不需要它)
  • ))
  • 无法与默认的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
    }
    

    }

其他推荐答案

如果您在应用程序中结束工作,则可以使用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 }
}

,基于此,为INT,Long等创建类似类的类型应该很容易使用它,将类型添加到映射封闭中:

class MyDomainClass {

  String name
  String otherField

  static mapping = {
    name type: EncryptedString
    otherField type: EncryptedString
  }
}

我省略了cryptoutils.encrypt()和cryptoutils.decrypt()方法,因为这不是特定于圣杯的.我们正在使用AE,例如" cipher cipher = cipher.getInstance('aes/cbc/pkcs5padding').无论您最终使用什么,请确保它是2路加密,即不要使用SHA-256.

其他推荐答案

如果客户担心某人在硬盘驱动器上进行身体走开,然后使用完整的磁盘解决方案,例如 TrueCrypt 应该工作.如果担心流量会被嗅探,请看一下 this JDBC上SSL上的MySQL文档的一部分.请记住,如果某人妥协您的服务器所有下注均已关闭.

本文地址:https://www.itbaoku.cn/post/597621.html

问题描述

I'm working on a Grails 1.0.4 project that has to be released in less than 2 weeks, and the customer just came up with a requirement that all data in the database should be encrypted.

Since encryption of every database access in the application itself could take a lot of time and will be error prone, the solution I seek is some kind of encryption transparent to the application.

Is there a way to setup Hibernate to encrypt all data in all tables (except maybie the id and version columns) or should I seek a MySQL solution (we're using MySQL 5.0) ?

EDIT: Thanks for all of your posts for alternative solutions, if the customer changes mind it would be great. As for now, the requirement is "No plain text in the Database".

Second thing I'd like to point out is that I'm using Grails, for those not fammiliar with it, It's a convention over configuration, so even small changes to the application that are not by convention should be avoided.

推荐答案

Well it has been a long time since I've asked the question. In the meantime, thanks for all the answers. They were great when dealing with the original idea of encrypting the entire database, but the requirement changed to only encrypting sensitive user info, like name and address. So the solution was something like the code down below.

We've implemented an Encrypter which reads the encryption method from the record ( so there can be different encryption per record) and use it to connect transient duplicate fields to the ones encrypted in the database. The added bonus/drawbacks are:

  • The data is also encrypted in memory, so every access to the method getFirstName descrypts the data (I guess there is a way to cache decrypted data, but I dont need it in this case)
  • Encrypted fields cannot be used with default grails/hibernate methods for search through database, we've made custom methods in services that get data, encrypt it and then use the encrypted data in the where clause of a query. It's easy when using User.withCriteria

    class User {

    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
    }
    

    }

其他推荐答案

If you end doing the work in the application, you can use Hibernate custom types and it wouldn't add that many changes to your code.

Here's an encrypted string custom type that I've used:

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 }
}

and based on this it should be simple to create similar classes for int, long, etc. To use it, add the type to the mapping closure:

class MyDomainClass {

  String name
  String otherField

  static mapping = {
    name type: EncryptedString
    otherField type: EncryptedString
  }
}

I omitted the CryptoUtils.encrypt() and CryptoUtils.decrypt() methods since that's not Grails-specific. We're using AES, e.g. "Cipher cipher = Cipher.getInstance('AES/CBC/PKCS5Padding')". Whatever you end up using, make sure it's a 2-way crypto, i.e. don't use SHA-256.

其他推荐答案

If the customer is worried about someone physically walking away with the hard drive then using a full disk solution like Truecrypt should work. If there worried about traffic being sniffed then take a look at this part of the mysql documentation on ssl over JDBC. Remember if someone compromises your server all bets are off.