序列化加密字段

在Coursera安全顶峰项目的验尸讨论中提出了一个疯狂的想法。 类可以在序列化期间自行加密吗?

这主要是学术上的“假设”练习。 很难想到这样一种情况,我们希望在持久性期间依靠对象自加密而不是使用显式加密机制。 我只能确定一种情况,我们不能简单地使类无法序列化:

HTTPSession钝化

Appserver可以钝化不活动的HTTPSession,以节省空间或将会话从一台服务器迁移到另一台服务器。 这就是会话应该只包含可序列化对象的原因。 (在可以安装在单个服务器上的小型应用程序中,通常会忽略此限制,但是如果需要扩展或扩展实现,则会导致问题。)

一种方法(也是首选方法?)是使会话在钝化过程中将自身写入数据库,并在激活过程中重新加载自身。 实际保留的唯一信息是重新加载数据所需的内容,通常只是用户ID。 这给HTTPSession实现增加了一些复杂性,但是有很多好处。 一个主要好处是确保敏感信息被加密很简单。

这不是唯一的方法,某些站点可能更喜欢使用标准序列化。 一些应用服务器可能会将“实时”会话的序列化副本的副本保留在H2等嵌入式数据库中。 谨慎的开发人员可能希望确保敏感信息在序列化期间进行加密,即使它永远不会发生。

注意:可以有一个很强的论点,那就是敏感信息不应首先出现在会话中–仅在必要时检索它,并在不再需要时安全地丢弃它。

该方法

我采用的方法基于有效Java中的序列化一章。 从广义上讲,我们希望使用序列化代理来处理实际的加密。 该行为是:

行动 方法 受保护的序列化类 序列化代理
序列化 writeReplace() 创建代理 不适用
writeObject() 抛出异常 将加密的内容写入ObjectOutputStream
反序列化 readObject() 从ObjectInputStream读取加密的内容
readResolve() 构造受保护的类对象

调用反序列化方法时,受保护的类引发异常的原因是,它防止了攻击者生成的序列化对象的攻击。 请参阅上述书籍中有关虚假字节流攻击和内部字段盗窃攻击的讨论。

这种方法有很大的局限性-如果没有子类重新实现代理,则无法扩展该类。 我认为这不是实际问题,因为该技术仅用于保护包含敏感信息的类,并且很少希望添加超出设计人员期望的方法的方法。

代理类处理加密。 下面的实现显示了使用随机盐(IV)和加密强消息摘要(HMAC)来检测篡改。

代码

public class ProtectedSecret implements Serializable {private static final long serialVersionUID = 1L;private final String secret;/*** Constructor.* * @param secret*/public ProtectedSecret(final String secret) {this.secret = secret;}/*** Accessor*/public String getSecret() {return secret;}/*** Replace the object being serialized with a proxy.* * @return*/private Object writeReplace() {return new SimpleProtectedSecretProxy(this);}/*** Serialize object. We throw an exception since this method should never be* called - the standard serialization engine will serialize the proxy* returned by writeReplace(). Anyone calling this method directly is* probably up to no good.* * @param stream* @return* @throws InvalidObjectException*/private void writeObject(ObjectOutputStream stream) throws InvalidObjectException {throw new InvalidObjectException("Proxy required");}/*** Deserialize object. We throw an exception since this method should never* be called - the standard serialization engine will create serialized* proxies instead. Anyone calling this method directly is probably up to no* good and using a manually constructed serialized object.* * @param stream* @return* @throws InvalidObjectException*/private void readObject(ObjectInputStream stream) throws InvalidObjectException {throw new InvalidObjectException("Proxy required");}/*** Serializable proxy for our protected class. The encryption code is based* on https://gist.github.com/mping/3899247.*/private static class SimpleProtectedSecretProxy implements Serializable {private static final long serialVersionUID = 1L;private String secret;private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";private static final String HMAC_ALGORITHM = "HmacSHA256";private static transient SecretKeySpec cipherKey;private static transient SecretKeySpec hmacKey;static {// these keys can be read from the environment, the filesystem, etc.final byte[] aes_key = "d2cb415e067c7b13".getBytes();final byte[] hmac_key = "d6cfaad283353507".getBytes();try {cipherKey = new SecretKeySpec(aes_key, "AES");hmacKey = new SecretKeySpec(hmac_key, HMAC_ALGORITHM);} catch (Exception e) {throw new ExceptionInInitializerError(e);}}/*** Constructor.* * @param protectedSecret*/SimpleProtectedSecretProxy(ProtectedSecret protectedSecret) {this.secret = protectedSecret.secret;}/*** Write encrypted object to serialization stream.* * @param s* @throws IOException*/private void writeObject(ObjectOutputStream s) throws IOException {s.defaultWriteObject();try {Cipher encrypt = Cipher.getInstance(CIPHER_ALGORITHM);encrypt.init(Cipher.ENCRYPT_MODE, cipherKey);byte[] ciphertext = encrypt.doFinal(secret.getBytes("UTF-8"));byte[] iv = encrypt.getIV();Mac mac = Mac.getInstance(HMAC_ALGORITHM);mac.init(hmacKey);mac.update(iv);byte[] hmac = mac.doFinal(ciphertext);// TBD: write algorithm id...s.writeInt(iv.length);s.write(iv);s.writeInt(ciphertext.length);s.write(ciphertext);s.writeInt(hmac.length);s.write(hmac);} catch (Exception e) {throw new InvalidObjectException("unable to encrypt value");}}/*** Read encrypted object from serialization stream.* * @param s* @throws InvalidObjectException*/private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException, InvalidObjectException {s.defaultReadObject();try {// TBD: read algorithm id...byte[] iv = new byte[s.readInt()];s.read(iv);byte[] ciphertext = new byte[s.readInt()];s.read(ciphertext);byte[] hmac = new byte[s.readInt()];s.read(hmac);// verify HMACMac mac = Mac.getInstance(HMAC_ALGORITHM);mac.init(hmacKey);mac.update(iv);byte[] signature = mac.doFinal(ciphertext);// verify HMACif (!Arrays.equals(hmac, signature)) {throw new InvalidObjectException("unable to decrypt value");}// decrypt dataCipher decrypt = Cipher.getInstance(CIPHER_ALGORITHM);decrypt.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(iv));byte[] data = decrypt.doFinal(ciphertext);secret = new String(data, "UTF-8");} catch (Exception e) {throw new InvalidObjectException("unable to decrypt value");}}/*** Return protected object.* * @return*/private Object readResolve() {return new ProtectedSecret(secret);}}
}

毋庸置疑,加密密钥不应如图所示进行硬编码或可能甚至进行缓存。 这是一条捷径,可以让我们专注于实施的细节。

密码和消息摘要应使用不同的密钥。 如果使用相同的密钥,则将严重损害系统的安全性。

任何生产系统中都应处理另外两件事:密钥轮换以及更改密码和摘要算法。 前者可以通过在有效负载中添加“密钥ID”来处理,后者可以通过绑定序列化版本号和密码算法来处理。 例如,版本1使用标准AES,版本2使用AES-256。 解串器应能够处理旧的加密密钥和密码(在合理范围内)。

测试码

测试代码很简单。 它创建一个对象,对其进行序列化,反序列化,然后将结果与原始值进行比较。

public class ProtectedSecretTest {/*** Test 'happy path'.*/@Testpublic void testCipher() throws IOException, ClassNotFoundException {ProtectedSecret secret1 = new ProtectedSecret("password");ProtectedSecret secret2;byte[] ser;// serialize objecttry (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutput output = new ObjectOutputStream(baos)) {output.writeObject(secret1);output.flush();ser = baos.toByteArray();}// deserialize object.try (ByteArrayInputStream bais = new ByteArrayInputStream(ser); ObjectInput input = new ObjectInputStream(bais)) {secret2 = (ProtectedSecret) input.readObject();}// compare values.assertEquals(secret1.getSecret(), secret2.getSecret());}/*** Test deserialization after a single bit is flipped.*/@Test(expected = InvalidObjectException.class)public void testCipherAltered() throws IOException, ClassNotFoundException {ProtectedSecret secret1 = new ProtectedSecret("password");ProtectedSecret secret2;byte[] ser;// serialize objecttry (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutput output = new ObjectOutputStream(baos)) {output.writeObject(secret1);output.flush();ser = baos.toByteArray();}// corrupt ciphertextser[ser.length - 16 - 1 - 3] ^= 1;// deserialize object.try (ByteArrayInputStream bais = new ByteArrayInputStream(ser); ObjectInput input = new ObjectInputStream(bais)) {secret2 = (ProtectedSecret) input.readObject();}// compare values.assertEquals(secret1.getSecret(), secret2.getSecret());}
}

最后的话

我不能过分强调–这主要是一种智力活动。 像往常一样,最大的问题是密钥管理,而不是密码学,并且由于前者需要付出的努力,您可能可以更快地实现更传统的解决方案。

在某些情况下,这可能仍然“足够好”。 例如,您可能只需要在长时间运行的应用程序期间保留数据。 在这种情况下,您可以在启动时创建随机密钥,并在程序结束后直接丢弃所有序列化的数据。

  • 源代码: https : //gist.github.com/beargiles/90182af6f332830a2e0e

翻译自: https://www.javacodegeeks.com/2015/06/auto-encrypting-serializable-classes.html

序列化加密字段

序列化加密字段_自动加密可序列化的类相关推荐

  1. java对象序列化去掉字段_使用序列化查找对象中的脏字段

    java对象序列化去掉字段 假设您正在开发一个将对象自动保存到数据库中的框架. 您需要检测两次保存之间所做的更改,以便仅保存已修改的字段. 如何检测脏场. 最简单的方法是遍历原始数据和当前数据,并分别 ...

  2. 序列化与反序列化_分布式系统基础之序列化和反序列化

    1. 了解序列化的意义 Java 平台允许我们在内存中创建可复用的 Java 对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在, 即,这些对象的生命周期不会比 JVM 的生命周期更长.但 ...

  3. java序列化如何实现_什么是java序列化,如何实现java序列化

    Java串行化技术可以使你将一个对象的状态写入一个Byte流里,并且可以从其它地方把该Byte流里的数据读出来,重新构造一个相同的对象.这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数 ...

  4. 共享文件夹加密专家_文件加密软件哪家靠谱?

    软件加密行业,早已有许多类型的加密软件.公司文件需要做好保密措施,尤其需求文件加密软件.近些年来必不可少的文件加密软件发展非常的好,尤其随着技术的进一步提升,所能为文件管理实现的保障价值更全面和更完善 ...

  5. lua加密教程_我们相信加密! 教程

    lua加密教程 许多人认为加密是一个复杂的主题,这很难理解. 可以实现其某些方面,但是每个人都可以理解它在更高层次上的工作方式. 这就是我要处理的这篇文章. 用简单的术语解释它是如何工作的,然后使用一 ...

  6. java序列化的方法_【Java常见序列化与反序列方法总结】

    人和电脑在很多方面都是十分相似的,大脑可以看成电脑主机,五官/身体等表面器官就是显示器.鼠标等外设.这篇文章就是想把计算机跟人做类比YY一下序列化和反序列化的机制.用途. 如果你是初学者,心里肯定会问 ...

  7. 加密解密_作业-加密解密程序

    # 加密解密程序'''作业:自己写一个加密程序,能够加密的内容是英文和汉字.同时加密并且解密就是说,一段话中既有中文又有英文,标点符号不用处理.加密规则,获取ascii码数字,中间用|分割# 思路提示 ...

  8. 毕业设计_Android短信查询及加密系统_会话加密

    下面是我的毕业设计,是一个Android的短信查询系统,系统支持根据短信号码.短信内容.短信发生时间进行多条件的短信查询.支持检索条件的与运算和或运算.扩展了会话加密.转发/存短信.收/发短信等功能. ...

  9. ibe加密原理_非对称加密如何实现一对多的加密方式?

    fuzzy-IBE以及ABE,可以实现一加多解(一份密文被多个不同的私钥解密),也可以多加一解(多个不同公钥加密的不同密文可以被同一个私钥解密). 回来写一个Fuzzy-IBE的介绍,从哪里说起呢,从 ...

最新文章

  1. python gevent 协程
  2. SharePoint 2013 配置HTTPS(SSL)
  3. 辨析Java与Javascript
  4. 《Too Much Heaven》
  5. X-Mas Musings –在Grails集成测试中不要使用随机服务器端口
  6. 工业交换机的性能优势有哪些?
  7. 从源码看ConcurrentHashMap
  8. XTU 1336 Perfect Palindrome Number
  9. 快门光圈感光度口诀_曝光补偿怎么调,快门光圈感光度口诀,深度解析曝光补偿...
  10. exchange server 2007 边缘传输服务器 垃圾邮件,边缘传输 启用反垃圾邮件更新向导 启用反垃圾邮件更新页...
  11. 运维派网站数据迁移过程踩到的坑和教训
  12. 前端——用div画菜鸟网站首页导航条“小三角”
  13. Android输入框下拉列表
  14. C语言怎么把单词拆成字母,如何用C语言切出汉语单词和英语单词混合字符串
  15. Quartz+spring列子
  16. linux系统打印机不兼容,linux系统下的打印机无法打印怎么解决?
  17. 补天SRC漏洞挖掘(一):主域名爬取
  18. 记一次JVM调优(Permanent Generation)
  19. CV领域Transformer这一篇就够了(原理详解+pytorch代码复现)
  20. 2011计算机2级,2011年度计算机等级考试二级VFP基础教程(41)

热门文章

  1. P4103-[HEOI2014]大工程【虚树,dp】
  2. P6047-丝之割【斜率优化,dp】
  3. jzoj4020-Revolution【网络流,最小割】
  4. P2519-[HAOI2011]problem a【dp】
  5. 【LCT】网络(luogu 2173/ZJOI2011)
  6. 【状压DP】剑之修炼(jzoj 2130)
  7. Spark SQL(十)之基于物品的推荐公式
  8. 深入理解Java ClassLoader及在 JavaAgent 中的应用
  9. JavaFX官方教程(七)之使用FXML创建用户界面
  10. 关于ByteBuffer使用解释