最近,我不得不将数据库加密添加到几个字段中,并且发现了很多不好的建议。

建筑问题

最大的问题是建筑。 如果持久性管理器悄悄地处理您的加密,那么根据定义,您的体系结构将在持久性和安全性设计之间要求紧密而不必要的绑定。 您不能触摸一个而不接触另一个。

这似乎是不可避免的,但是有一个受人尊敬的想法,那就是最好的架构是您拥有独立的应用程序开发人员和安全开发人员团队的架构。 应用程序开发人员不能草率,但总的来说,他们唯一的重点是功能完成。 安全开发人员负责设计和实现安全性。 唯一考虑这两个方面的地方是建筑和顶层设计。

过去这不是很实用,但是面向方面的编程(AOP)和类似的概念已经改变了这一点。 现在,在服务层和持久层之间注入一个拦截器是完全合理的,这样就可以悄悄地丢弃未授权调用方查看的值。 10个项目的列表可能会减少到7个,或者更新可能会引发异常,而不是修改只读值。 持久保存集合时要复杂一些,但是一般方法应该很明确。

这里的关键是,应用程序开发人员无需查看安全代码。 所有这些都可以通过在部署时通过配置文件添加的AOP注入来处理。 更重要的是,它可以随时更改,而无需修改应用程序本身。 (您可能需要执行一个更新过程,该过程将更改数据库中的值。)

拦截器甚至可以阻止对未记录方法的调用-不用担心流氓程序员。

在实践中,许多站点将有几个开发人员都戴上帽子,而不是拥有专门的安全团队。 只要他们能够牢记自己的职责,这不是问题。

在JPA或Hibernate字段中进行透明加密绝对比在POJO中放入加密/解密代码更好,但是它仍然在安全性和持久性层之间强加了不必要的绑定。 它还存在严重的安全问题。

安全问题

每当您处理加密时,都会遇到一个关键问题–可以将此对象写入磁盘吗? 最明显的威胁是序列化,例如,通过钝化数据以释放内存或将其迁移到其他服务器的应用服务器。

实际上,这意味着您的密钥和纯文本内容必须标记为“ transient”(对于序列化引擎)和“ @Transient”(对于JPA或Hibernate)。 如果您真的很偏执,您甚至会覆盖隐式序列化方法writeObject,因此可以绝对保证这些字段永远不会写入磁盘。

这是可行的……但是它使透明的加密/解密大为失败,因为该代码的全部目的是使这些字段看起来就像另一个字段。 您必须维护两个字段-持久加密值和瞬态未加密值-并具有某种使它们保持同步的方法。 无需在您的pojo中添加任何密码即可完成所有操作。

一个更微妙的问题是,如果攻击者可以通过使应用服务器崩溃而触发核心转储,则您的对象仍可能写入磁盘。 细心的站点管理员将禁用核心转储,但许多人忽略了它。 解决这个问题比较困难,但是如果AOP可以在需要解密值的方法周围立即解密/加密值,则有可能。 您的应用程序不关心解密在哪里发生,只要它在需要时就被解密即可。 这是应该留给安全团队的决策类型。

可以通过操作系统交换文件将对象写入磁盘的第三种方式,但这应该不是问题,因为交换文件现在通常已加密。

JPA实体侦听器

一个解决方案是JPA EntityListeners或相应的Hibernate类。 这些是侦听器类,可以提供在数据库对象创建,删除或修改之前或之后调用的方法。

样例代码

使用一些示例代码最容易看到这一点。 考虑一种情况,我们必须保留第三方站点的用户密码。 在这种情况下,我们必须使用加密,而不是哈希。

(注意:我怀疑这是Twitter第三方应用程序所需的实际信息–仅用于说明目的。)

实体

/*** Conventional POJO. Following other conventions the sensitive* information is written to a secondary table in addition to being* encrypted.*/
@Entity
@Table(name='twitter')
@SecondaryTable(name='twitter_pw', pkJoinColumns=@PrimaryKeyJoinColumn(name='twitter_id'))
@EntityListeners(TwitterUserPasswordListener.class)
public class TwitterUser {private Integer id;private String twitterUserprivate String encryptedPassword;transient private String password;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)public Integer getId() { return id; }@Column(name = 'twitter_user')public String getTwitterUser() { return twitterUser; }@Column(name = 'twitter_pw', table = 'twitter_pw')@Lobpublic String getEncryptedPassword() { return encryptedPassword; }@Transientpublic String getPassword() { return password; }// similar definitions for setters....
}

DAO

/*** Conventional DAO to access login information.*/
@LocalBean
@Stateless
public class TwitterDao {@PersistenceContextprivate EntityManager em;/*** Read an object from the database.*/@TransactionAttribute(TransactionAttributeType.SUPPORTS)public TwitterUser getUserById(Integer id) {return em.find(TwitterUser.class, id);}/*** Create a new record in the database.*/@TransactionAttribute(TransactionAttributeType.REQUIRED)public saveTwitterUser(TwitterUser user) {em.persist(user);}/*** Update an existing record in the database.** Note: this method uses JPA semantics. The Hibernate* saveOrUpdate() method uses slightly different semantics* but the required changes are straightforward.*/@TransactionAttribute(TransactionAttributeType.REQUIRED)public updateTwitterUser(TwitterUser user) {TwitterUser tw = em.merge(user);// we need to make one change from the standard method -// during a 'merge' the old data read from the database// will result in the decrypted value overwriting the new// plaintext value - changes won't be persisted! This isn't// a problem when the object is eventually evicted from// the JPA/Hibernate cache so we're fine as long as we// explicitly copy any fields that are hit by the listener.tw.setPassword(user.getPassword());return tw;}

EntityListener

为了在持久层和安全层之间保持清晰的隔离,侦听器除了调用处理加密的服务外什么也不做。 它完全不了解加密细节。

public class TwitterUserPasswordListener {@Injectprivate EncryptorBean encryptor;/*** Decrypt password after loading.*/@PostLoad@PostUpdatepublic void decryptPassword(Object pc) {if (!(pc instanceof TwitterUser)) {return;}TwitterUser user = (TwitterUser) pc;user.setPassword(null);if (user.getEncryptedPassword() != null) {user.setPassword(encryptor.decryptString(user.getEncryptedPassword());}}/*** Decrypt password before persisting*/@PrePersist@PreUpdatepublic void encryptPassword(Object pc) {if (!(pc instanceof TwitterUser)) {return;}TwitterUser user = (TwitterUser) pc;user.setEncryptedPassword(null);if (user.getPassword() != null) {user.setEncryptedPassword(encryptor.encryptString(user.getPassword());}}
}

EncryptorBean

EncryptorBean处理加密,但不知道正在加密什么。 这是一个最小的实现–在实践中,我们可能会希望除了密文/明文之外还传递一个keyId。 这将使我们能够以最小的干扰安静地旋转加密密钥-这是通常的“简单加密”方法绝对不可能实现的。

此类使用OWASP / ESAPI进行加密,因为1)它应已由您的应用程序使用; 2)可移植格式允许其他应用程序使用我们的数据库,只要它们也使用OWASP / ESAPI库即可。

该实现仅涵盖字符串-健壮的解决方案应具有针对所有原始类型以及可能针对特定领域的类(例如信用卡)的方法。

import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Encryptor;
import org.owasp.esapi.codecs.Base64;
import org.owasp.esapi.crypto.CipherText;
import org.owasp.esapi.crypto.PlainText;
import org.owasp.esapi.errors.EncryptionException;
import org.owasp.esapi.reference.crypto.JavaEncryptor;@Stateless
public class EncryptorBean {private static final String PBE_ALGORITHM = 'PBEWITHSHA256AND128BITAES-CBC-BC';private static final String ALGORITHM = 'AES';// hardcoded for demonstration use. In production you might get the// salt from the filesystem and the password from a appserver JNDI value.private static final String SALT = 'WR9bdtN3tMHg75PDK9PoIQ==';private static final char[] PASSWORD = 'password'.toCharArray();// the keyprivate transient SecretKey key;/*** Constructor creates secret key. In production we may want* to avoid keeping the secret key hanging around in memory for* very long.*/public EncryptorBean() {try {// create the PBE keyKeySpec spec = new PBEKeySpec(PASSWORD, Base64.decode(SALT), 1024);SecretKey skey = SecretKeyFactory.getInstance(PBE_ALGORITHM).generateSecret(spec);// recast key as straightforward AES without padding.key = new SecretKeySpec(skey.getEncoded(), ALGORITHM);} catch (SecurityException ex) {// handle appropriately...}}/*** Decrypt String*/public String decryptString(String ciphertext) {String plaintext = null;if (ciphertext != null) {try {Encryptor encryptor = JavaEncryptor.getInstance();CipherText ct = CipherText.from PortableSerializedBytes(Base64.decode(ciphertext));plaintext = encryptor.decrypt(key, ct).toString();} catch (EncryptionException e) {// handle exception. Perhaps set value to null?}}return plaintext;}/*** Encrypt String*/public String encryptString(String plaintext) {String ciphertext= null;if (plaintext!= null) {try {Encryptor encryptor = JavaEncryptor.getInstance();CipherText ct = encryptor.encrypt(key, new PlaintText(plaintext));ciphertext = Base64.encodeBytes(ct.asPortableSerializedByteArray());} catch (EncryptionException e) {// handle exception. Perhaps set value to null?}}return ciphertext;}
}

最后的想法

没有理由为什么未加密字段和加密字段之间必须具有一对一的关系。 将相关字段捆绑为一个值是完全合理的-实际上,最好单独加密每个字段。 这些值可以用CSV,XML,JSON甚至属性文件表示。

参考: Invariant Properties博客中的JCG合作伙伴 Bear Giles 使用JPA侦听器进行数据库加密 。

翻译自: https://www.javacodegeeks.com/2012/11/database-encryption-using-jpa-listeners.html

使用JPA侦听器的数据库加密相关推荐

  1. jpa加密_使用JPA侦听器的数据库加密

    jpa加密 最近,我不得不将数据库加密添加到一些字段中,并且发现了很多不好的建议. 建筑问题 最大的问题是建筑. 如果持久性管理器静静地处理您的加密,那么根据定义,您的体系结构将在持久性和安全性设计之 ...

  2. vue 侦听器侦听对象属性_Spring中的异步和事务性事件侦听器

    vue 侦听器侦听对象属性 内置的事件发布功能从Spring的早期版本开始存在,并且对于处理同一应用程序上下文中Spring组件之间的基本通信仍然有用. 通常,应用程序可以生成应用程序事件(可以是任意 ...

  3. Spring中的异步和事务性事件侦听器

    内置的事件发布功能从Spring的早期版本开始存在,对于处理同一应用程序上下文中Spring组件之间的基本通信仍然有用. 通常,应用程序可以生成应用程序事件(可以是任意对象)并侦听它们. 整个机制非常 ...

  4. java从外部得到数据_java – 如何实现Observer以从侦听器获取数据?

    我正在使用 MaterialDrawer库为我的应用程序创建一个简单的抽屉,库中的一些类实例需要在调用时传递给它们的字符串.一个例子是IProfile类: IProfile profile = new ...

  5. 怎么配置 Oracle 侦听器来使用SQL操作ST_Geometry

    关于这个内容,其实从ArcSDE9.2推出ST_Geometry就让用户感到很有吸引力,而且特别是在ArcSDE9.3之后,用户使用SQL操作ST_geometry越来越多,但是在配置Oracle监听 ...

  6. vue 侦听器侦听对象属性_SQL Server始终处于侦听器状态

    vue 侦听器侦听对象属性 This article on SQL Server Always On Listeners includes an overview and various connec ...

  7. ServletContextListener Servlet侦听器示例

    ServletContextListener is one of the many Servlet Listener we have. This is the fifth article in the ...

  8. 严重: 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.co

    原文 BeanFactory创建Bean实例错误,原因可能是项目的builderpath中的JDK版本莫名被调成默认的了,如javase1.5,重新移除添加系统的jdk即可. 2022.2.14 补充 ...

  9. 严重:异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener] 以解决

    严重: 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener] org.springframewor ...

最新文章

  1. nodejs Yarn替代npm的包管理——快速、安全、可靠性高的依赖管理
  2. SQLserver2008高级查询语句应用实例
  3. Centos 安装Eclipse报错java.lang.UnsatisfiedLinkError: Could not load SWT library
  4. 7.30 KubeCon2020 | 今天下午5:40 近几年最火爆的技术峰会之分享主题.NET开发者与Kuberentes...
  5. java jdk文档查询方法_查询Java JDK文档的元数据
  6. codeforces 719A Vitya in the Countryside(序列判断趋势)
  7. 如何调节电脑显示屏来保护双眼的小技巧
  8. linux sed用法
  9. EF Core in Action 中文翻译 第一部分导航
  10. 电脑 手机 模拟器IP地址怎么更换
  11. 电子设计大赛-室内可见光定位装置
  12. linux三星电脑开机怎么进入页面,三星Samsung笔记本电脑开机进入BIOS的方法与BIOS设置全功能菜单(F2)...
  13. displayport1.4
  14. 2019第十届蓝桥杯——I.胖子迷宫
  15. week6:Diagnosing Bias vs. Variance难点记录
  16. 登录接口已修复梦想贩卖机V2 2.0.4 修复版,附带安装教程。
  17. Android应用开发接入讯飞语音合成
  18. 学英语《每日一歌》之yesterday once more
  19. mybatis实现动态模糊查询
  20. 应用包含Involution算子的RedNet实现Classification

热门文章

  1. java本地监听zk服务器节点【动态上下线】
  2. LNCS用户写作指南【 Springer Computer Science Proceedings 】
  3. java程序连接kafka_Java的Kafka:构建安全,可扩展的消息传递应用程序
  4. java rest框架_比较Java REST文档框架
  5. 视图中::text_新CalendarFX视图:MonthGridView!
  6. 使用所有对象共有的方法
  7. 您准备好观看GraphQL了吗?
  8. 轻松与外来客户进行REST通信
  9. 进程比线程更多资源_为什么我们不应该使用比我们需要更多的线程
  10. ElasticSearch初学者教程