在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. 序列化加密字段_自动加密可序列化的类

    序列化加密字段 在Coursera安全顶峰项目的验尸讨论中提出了一个疯狂的想法. 类可以在序列化期间自行加密吗? 这主要是学术上的"假设"练习. 很难想到这样一种情况,我们希望在持 ...

  2. 关于django—模型序列化器类详细内容

    一.定义模型序列化器类 1.继承serializers.ModelSerializer类或其子类 2.需要在Meta内部类中指定model.fields类属性参数 3.model指定模型类(需要生成序 ...

  3. Django 基础(13)-Django drf 序列化器类to_representation和to_internal_value(处理返回的日期格式)、序列化类 ModelSerializer

    文章目录 一.Django drf 序列化 1. 背景 2. 使用思路 3. 代码demo 4. [重要]序列化类 ModelSerializer 5. DRF序列化器to_representatio ...

  4. 【Unity】解析Excel数据,并自动创建对应的C#类

    升级版传送门: [Unity]升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_萧然CS的博客-CSDN博客Excel注释操作: ...

  5. 【Flask项目2】python对象分页数据序列化基类(5)

    comment-utils-serializer.py文件 class BasePaginateSerializer(object):"""分页数据序列化基类" ...

  6. 让Visual Studio 2013为你自动生成XML反序列化的类

    Visual Sutdio 2013增加了许多新功能,其中很多都直接提高了对代码编辑的便利性.如: 1. 在代码编辑界面的右侧滚动条上显示不同颜色的标签,让开发人员可以对所编辑文档的修改.查找.定位情 ...

  7. 自动加密企业关键业务数据 赛门铁克推出全新信息保护解决方案

    最新推出的Symantec Information Centric Security解决方案,能够帮助企业随时随地对数据进行自动加密.跟踪和撤销,提供卓越的可见性和管控力 近日,全球网络安全领域的领导 ...

  8. baseresponse响应类_内部类、响应类Response、序列化基类、反序列化、全局局部钩子...

    一.内部类 1.概念:将类定义在一个类的内部,被定义的类就是内部类 2.特点:内部类及内部类的所以名称空间,可以直接被外部类访问的 3. 应用:通过内部类的名称空间,给外部类额外拓展一些特殊的属性(配 ...

  9. java-对密码进行加密和验证的类

    java 密码MD5加密 java加密bytestring算法null package com.sunnylocus.util;      import java.security.MessageDi ...

最新文章

  1. TCP UDP 协议深度解析 (未完待续)
  2. iOS 自定义 View
  3. 递归神经网络部分组件(七)
  4. vue报错vue-router.esm.js?8c4f:2062 Uncaught (in promise) Error: Avoided redundant navigation to curren
  5. SDK 和 API
  6. linux安装.AppImage后缀安装包
  7. 江苏省消保委约谈14家企业,要求确保弹窗一键关闭无障碍实现
  8. Spring MVC PathVariable
  9. 2017php类库,AMQB官方PHP库
  10. rocketMq消息重复消费问题
  11. 编程基础(一)——计算机中的数
  12. SmtpClient 类
  13. C语言的本质(19)——预处理之一:宏定义
  14. 广数系统加工中心编程_数控加工中心编程技巧一文通
  15. 软件工程毕业答辩常问的问题
  16. php 获取城市列表接口,省份城市区域列表
  17. 手机上最好用的五笔输入法_什么手机输入法最实用?目前最受欢迎的3款盘点,你正在用哪款呢...
  18. uni-app学习路线与建议
  19. make px4fmu-v2_default报错:ninja:no work to do
  20. stm32+esp8266 GET请求心知天气的简单方法

热门文章

  1. 《走遍中国》珍藏版(十三)
  2. JavaScript实现搜索框效果
  3. 什么是mysql的主从复制?
  4. 用数组模拟队列的实现
  5. 猜数字游戏 : 共给玩家10次机会,若第一次就猜对了,显示‘您真是个天才’,若10也没猜对,显示“您太笨了,下次努力吧!”, 若是第2-10次猜对了,只简单的显示:“恭喜您猜对了”。
  6. 查看电脑重启日志_系统日志看硬盘故障图文教程,电脑日志查看磁盘硬盘坏道问题方法...
  7. 使用log4j2打印mybatis的sql执行日志
  8. 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)
  9. spring 注释_Spring @Value注释
  10. java fastutil_具有FastUtil的精简Java集合