与Java的序列化机制有关的问题已广为人知。 有效的Java 1st Edition (第10章)和有效的Java 2nd Edition (第11章)的整个最后一章都专门讨论Java的序列化主题。 Effective Java 3rd Edition (第12章)的最后一章仍致力于序列化,但是其中包括一个新项目(Item 85) ,该项目甚至进一步强调了与Java序列化有关的两个断言 :
- “ 避免序列化攻击的最佳方法是永远不要反序列化任何东西。 “
- “ 您没有理由在您编写的任何新系统中使用Java序列化。 “
在最近发布的文档“ 迈向更好的序列化 ”中,Brian Goetz“探讨了改善Java平台中序列化的可能方向。” 尽管本文档的主要目的是为Java序列化提出潜在的新方向,但它只是“一个探索性文档,并不构成任何特定功能的计划。” 这意味着对于Java序列化可能采取的方向来说,这是一个有趣的读物,但是阅读本文档对于Java序列化的总结(当前存在的意义以及我们如何到达此地)具有重要的价值。 这是我其余文章的主题,在本文中我将参考并总结“ 迈向更好的序列化 ”的各个部分,这些部分使我感觉最清楚地阐明了Java序列化机制的当前问题以及我们为什么遇到这些问题。
Goetz在Java序列化的“悖论”中引人注目的段落打开了文档的“动机”部分:
Java的序列化工具有点自相矛盾。 一方面,这可能对Java的成功至关重要-没有它,Java可能不会占据统治地位,因为序列化实现了透明的远程处理,进而实现了Java EE的成功。 另一方面,Java的序列化几乎使每一个错误都可以想象到,并给库维护人员,语言开发人员和用户带来持续的税负(以维护成本,安全风险和缓慢的发展为形式)。
Goetz文档“动机”部分的另一段区分了序列化的一般概念和Java当前的序列化机制的特定设计 :
需要明确的是,
序列化的概念 ; 将对象转换为可以轻松跨JVM传输并在另一侧重构的形式的能力是一个非常合理的想法。 问题出在
Java中的序列化设计 ,以及它如何适合(或更确切地说,不适合)对象模型。
Goetz指出“ Java的序列化(错误)是多方面的”,他概述了Java序列化设计所犯的“部分罪过”。 我强烈建议阅读原始文档 ,以获取对这些“罪过”的简明和说明性描述,此处仅作总结。
- “伪装成图书馆的功能,但不是。”
- “序列化伪装成一个库功能。
- “假装是静态类型的功能,但不是。”
- “可序列化性是对象的动态类型的函数,而不是其静态类型的函数。”
- “编译器无济于事”指出“编写可序列化类时可能犯的各种错误”
- “魔术方法和字段”是“不影响序列化行为的任何基类或接口指定的”
- “绝对必要。”
- “紧密结合到编码。”
- “不幸的流格式”,“既不紧凑,也不高效,也不可读”。
Goetz还概述了这些Java序列化设计决策的后果(有关每个“严重问题”的更多背景,请参阅原始文档 ):
- “使图书馆维护者瘫痪。”
- “库设计人员在发布可序列化的类之前必须非常仔细地考虑-因为这样做可能使您维护与曾经被序列化的所有实例的兼容性。”
“嘲笑封装。”
- “串行化是您内部状态的无形但公共的构造函数,以及一组无形但公共的访问器。”
在Goetz的“ 迈向更好的序列化 ”文档中,我最喜欢的部分可能是“潜在的错误”部分,因为Goetz在本部分中概述的项目是我编写,阅读和使用的其他Java代码中错误的常见原因。 换句话说,尽管Goetz特别讨论了这些设计决策如何导致Java的序列化机制出现问题,但我(毫无疑问)发现这些通用设计决策也导致了其他领域的问题。
Goetz用以下语句打开“潜在的错误”部分:“上面列出的许多设计错误都来自一个共同的来源-选择通过“魔术”实现序列化,而不是将解构和重建放在对象的第一位。模型本身。” 我发现由其他开发人员甚至我自己编写的“魔术”代码在以后常常令人困惑且难以推理。 我已经明确地意识到,通常最好使用简洁明了的代码。
格茨补充说:“更糟糕的是,魔术尽了最大努力,以使读者看不见。” 当我们第一次实现隐形的“魔术”设计时,它们通常看起来很聪明,但是当他们突然需要对基础魔术的可见性时,使必须阅读,维护和更改代码的开发人员感到非常痛苦。
Goetz引用了Edsger W.Dijkstra的话,并写道:“序列化(目前已实现)与减少程序文本和其计算效果之间的差距完全相反; 我们可能会错误地假设我们的对象总是由类中编写的构造函数初始化而得到原谅,但我们不必如此。
Goetz在“底层错误”部分的结尾处开始了一段,“序列化除了试图变得不可见之外,还尝试做太多事情 。 尽管Goetz专门针对Java的序列化编写了当前的“序列化程序 (而不是仅仅序列化数据 )”的文章,但从更广泛的意义上讲,我已经无数次地看到了这个问题。 对于我们的开发人员来说,设计和实现可以执行某些我们认为可能对某人有用的小功能的代码很诱人,即使绝大多数(或什至所有当前已知的)用户和用例只需要一个简单的子集即可。功能。
鉴于“ 迈向更好的序列化 ”的目标是“探索改善Java平台中序列化的可能方向”,因此文档中涉及到可能影响Java未来序列化机制的设计甚至实现细节的重要细节也就不足为奇了。 此外, Project Amber邮件列表( amber-dev和amber-spec-experts )也对Java序列化的未来发展方向进行了重要讨论。 但是,本文的目的不是看Java序列化的未来,而是着眼于本文档如何很好地总结了Java当前的序列化机制及其历史。
尽管前面提到的Project Amber邮件列表中的消息集中在Java的序列化机制的潜在未来上,但是这些帖子中有关Java当前序列化的一些有趣的评论增加了Goetz在“ 迈向更好的序列化 ”中所总结的内容。 以下是一些最有趣的内容:
- Goetz在宣布“ 迈向更好的序列化 ” 的帖子中指出,该提案“从根本上解决了序列化的风险”,并“将对象序列化带到了光明的地方,为了确保安全性,必须要这样做。”
- Brian Goetz的帖子通过暗示重申了当今Java序列化问题的很大一部分是在不调用构造函数的情况下构造对象:“我们的主要安全目标[是允许]反序列化[通过]构造函数进行。”
- 斯图尔特·马克斯(Stuart Marks)的帖子指出:“提案中关于便利性的推理路线并不是说便利本身就是邪恶的,而是为了追求便利,原始设计采用了语言学机制来实现这一目的。 这削弱了Java平台的某些基础,并直接导致了多个错误和安全漏洞,其中一些是我个人修复的。”
- Marks概述了一些由于序列化相关的设计决策而导致JDK中细微错误的特定示例。
- Kevin Bourrillion的帖子指出:“ Java的序列化实现很长一段时间以来一直是一个巨大的伤口”,并补充说“支持其他有线格式的每个框架始终必须从头开始。”
我强烈建议任何对Java序列化感兴趣的人阅读“ 迈向更好的序列化 ”,无论他们的主要兴趣是Java当前的序列化机制还是将来可能变成的东西。 从两个角度来看,这都是一个有趣的文档。
翻译自: https://www.javacodegeeks.com/2019/06/motivations-behind-javas-maligned-serialization.html