序列化加密字段_自动加密可序列化的类

序列化加密字段

在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

序列化加密字段

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337981.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言if与else匹配规则你知道多少?

点击上方蓝字关注我,了解更多咨询如果程序中有多个if和else,那如何配对,if和else的匹配规则简单总结为以下两点:1.无花括号,else和最近的if匹配2.有花括号,else和内含有if语句的第1个if语句匹配我们通过两个…

cobertura_Cobertura和Sonar 5.1的问题

cobertura最近,我有些麻烦,试图在我的Grails 2.4.4项目中使用Sonar 5.1。 我使用的是Groovy常用的东西: Gmetrics,Codenarc和Cobertura 。 对于Sonar数据库,我使用的是Postgres 9.4 。 声纳跑步者的日志文件给了我这个…

C语言中+=的含义你明白吗?

点击上方蓝字关注我,了解更多咨询c语言中是代表两个变量相加,并将答案记录在左边的变量里。比如xy实际代表的表达式为:xxy。这是c语言为了语言的简洁性推出的组合运算。常见的运算符都可以这样组合运算。一、源码演示:二、输出结果…

calcite mysql_Apache顶级项目 Calcite使用介绍

原标题:Apache顶级项目 Calcite使用介绍什么是CalciteApache Calcite是一个动态数据管理框架,它具备很多典型数据库管理系统的功能,比如SQL解析、SQL校验、SQL查询优化、SQL生成以及数据连接查询等,但是又省略了一些关键的功能&am…

C语言中「」的含义

点击上方蓝字关注我,了解更多咨询取决于在scanf中使用还是在printf中使用。在scanf中使用,则添加了*的部分会被忽略,不会被参数获取。例如:int a,b;char b[10];scanf("%d%*s",&a,b);输入为:1…

maven和docker_Java EE,Docker和Maven(技术提示#89)

maven和dockerJava EE应用程序通常使用Maven构建和打包。 例如&#xff0c; github.com/javaee-samples/javaee7-docker-maven是一个琐碎的Java EE 7应用程序&#xff0c;它显示了Java EE 7依赖性&#xff1a; <dependencies><dependency><groupId>javax<…

mysql ondumplictcate_4.5万字手把手教你实现MySQL TB级数据存储!!

写在前面业界对系统的高可用有着基本的要求&#xff0c;简单的说&#xff0c;这些要求可以总结为如下所示。系统架构中不存在单点问题。可以最大限度的保障服务的可用性。一般情况下系统的高可用可以用几个9来评估。所谓的几个9就是系统可以保证对外提供的服务的时间达到总时间…

c语言中+ =和=+有什么区别

点击上方蓝字关注我&#xff0c;了解更多咨询c语言中 和有什么区别&#xff1a;区别在于 是简写&#xff0c;a 1就是a a1&#xff1a;并不是简写&#xff0c;a a直接对a的赋值&#xff0c;符号代表的是正负&#xff08;完全可以省略不写&#xff09;&#xff0c;即a b其实就…

jvm7 jvm8_我们真的仍然需要32位JVM吗?

jvm7 jvm8即使在今天&#xff08;2015年&#xff09;&#xff0c;我们仍然有两个版本或Oracle HotSpot JDK –已调整为32或64位体系结构。 问题是我们是否真的想在服务器甚至笔记本电脑上使用32位JVM&#xff1f; 我们应该有很受欢迎的意见&#xff01; 如果只需要较小的堆&…

C语言中的printf函数,你觉得它简单吗?

点击上方蓝字关注我&#xff0c;了解更多咨询什么是printf函数&#xff1f;printf函数是C语言当中的输出函数&#xff0c;是用来将内容显示在屏幕上的&#xff0c;是C库中的一种函数。printf函数的用法printf函数使用起来很简单&#xff0c;格式&#xff1a;printf&#xff08;…

tp5怎么生成短链接_请问在tp5中怎样才能使用url函数?

MM们thinkphp中的自动完成函数调用有两种不同的方法&#xff0c;分别为callback与function;12345678910namespace Home\Model;use Think\Model;class UserModel extends Model{protected $_auto array (array(status,1), // 新增的时候把status字段设置为1array(password,md5,…

cuba 平台_CUBA平台的理念

cuba 平台最近发生了很多事。 在CUBA于6月1日正式发布之后&#xff0c;我们推出了新版本&#xff0c;在一些Java网站上发布了我们的第一篇文章&#xff0c;并在伦敦的Devoxx UK会议上介绍了该平台 。 但是在热潮继续之前&#xff0c;大约是时候阐明CUBA背后的哲学了。 与企业软…

C语言进阶:探讨函数指针的的定义与使用

点击上方蓝字关注我&#xff0c;了解更多咨询函数指针今天准备和大家讲解一下我们C语言编程的函数指针&#xff0c;为什么函数也需要指针呢&#xff1f;我是这样理解的函数指针方便我们对函数的调用&#xff0c;当我们需要把一个函数作为参数传给其他参数的时候就必须使用函数指…

jvm gc停顿_在JVM中记录世界停顿

jvm gc停顿不同的事件可能导致JVM暂停所有应用程序线程。 这种暂停称为世界停止&#xff08;STW&#xff09;暂停。 触发STW暂停的最常见原因是垃圾回收&#xff08; 例如github中的示例 &#xff09;&#xff0c;但是不同的JIT操作&#xff08; 示例 &#xff09;&#xff0c;…

-在c语言中什么含义

点击上方蓝字关注我&#xff0c;了解更多咨询->是一个整体&#xff0c;它是用于指向结构体。1.换种说法&#xff0c;如果我们在C语言中定义了一个结构体&#xff0c;然后申明一个指针指向这个结构体&#xff0c;那么我们要用指针取出结构体中的数据&#xff0c;就要用到“-&…

idea如何连接本地mysql_IDEA如何连接MYSQL

IDEA如何连接MYSQL1>下载JDBC2>导入数据库驱动在IDEA中新建一个lib目录复制下载的驱动包&#xff0c;然后粘贴到刚刚建立的lib目录下右键点击添加的数据库驱动&#xff0c;找到最下面的Add as Library...&#xff0c;点击它会出现一个弹窗&#xff0c;点击"OK"…

apache camel_Apache Camel日志组件示例

apache camelApache Camel日志组件示例 您要将消息记录到底层的记录机制中&#xff0c;请使用骆驼的log:组件。 Camel使用sfl4j作为记录器API&#xff0c;然后允许您配置记录器实现。 在本文中&#xff0c;我们将使用Log4j作为实际的记录器机制。 让我们从我们的例子开始。 依…

putchar在c语言中怎么用

点击上方蓝字关注我&#xff0c;了解更多咨询putchar在c语言中的作用是向终端输出一个字符&#xff0c;也属于一种C库函数&#xff0c;包含在C标准库中&#xff0c;putchar是一个字符输出&#xff0c;用于快写的时候很方便。首先来看一下c语言的含义&#xff0c;c语言是一门面向…

mysql设计体会_数据库设计心得体会

组名&#xff1a;NoobStruggle。成员&#xff1a;刘海天、胡亮、谭晓杰、宁君辉。一&#xff0e;分析需求对于每一个项目&#xff0c;数据库的设计都是至关重要的&#xff0c;它关系到后端进行接口开发时实现的难度&#xff0c;数据库中数据的可维护性&#xff0c;一致性&#…

hibernate查询缓存_Hibernate查询缓存如何工作

hibernate查询缓存介绍 现在&#xff0c;我已经介绍了实体和集合缓存&#xff0c;现在该研究查询缓存的工作原理了。 查询缓存与实体严格相关&#xff0c;它在搜索条件和满足该特定查询过滤器的实体之间绘制关联。 像其他Hibernate功能一样&#xff0c;查询缓存也不像人们想象…