Java|Java小知识:序列化过滤器

Java|Java小知识:序列化过滤器

JDK 9新增了序列化过滤器(Serialization Filter) , JDK 17又修改了一次 。
想必大多数java程序员还在用着Java 8吧?还是建议提前了解一下JDK 9/10/11/12/13/14/15/16啊 , 毕竟现在都17了呢 。
【Java|Java小知识:序列化过滤器】今天这篇介绍一下JDK 9中新增的序列化过滤器 , 虽然JDK 8里面用不了 , 但是了解一下它的思想 , 变通一下 , 然后在JDK 8里面使用 , 能解决不少问题呢 。
序列化过滤器究竟是什么呢?它是一种让java程序更好地控制请求数据(incoming data)的反序列化方式的工具 。 换句话说 , 序列化过滤器是用来控制请求数据的反序列化的功能 。
序列化中有什么问题Java序列化一直以来都是各种bug或问题的源头 , 包括:

  1. 破坏封装性
  2. 魔法字段和方法影响的行为 , 例如readObject writeObject readObjectNoData readResolve writeReplace等 。
  3. 性能慢 , 无法满足互联网服务的性能要求 , 所以大厂后端的rpc框架几乎不会采用java序列化 , 用的都是hessian、protobuf甚至json等 。
为什么需要自己控制反序列化?Java序列化除了上面所说的3个问题 , 最大的问题是反序列化 , 尤其是对不可信任数据的反序列化(不安全是因为破坏封装性和readObject writeObject等特殊方法导致的) 。 网上很大一部分漏洞都是因为反序列化了不安全的数据 。 假如有一个网站提供的API , 直接把请求参数反序列化为某一个类 , 然后再进行后面的处理 。 如果它的反序列化过程有安全漏洞 , 那么黑客就可以构造出一个特殊的请求参数 , API按照正常流程处理这个请求 , 反序列化 , 就会触发漏洞去加载黑客提供的某一个类(包含恶意代码) , 从而导致网站被攻击 。
所以Oracle一直强调 , “不要反序列化不可信任的数据”(参考Secure Coding Guidelines for Java SE)
另外 , 阿里fastjson经常爆出各种漏洞 , 绝大部分都与反序列化有关(光顾着追求快 , 基本的东西都弄丢了吧)
正是因为这些问题(破坏封装、readObject和不安全性) , JDK 9增加了序列化过滤器 。
如何配置序列化过滤器?序列化过滤器可以通过一个pattern来配置 , 而这个pattern可以基于:
  1. 对象图的最大深度
  2. 对象图的最大引用数
  3. 对象图的最大字节数
  4. 最大数组大小
  5. 类名、包名和模块名的黑白名单
具体如何构造这个pattern , 可以参考此文Serialization Filtering 。
构造好了pattern及对应过滤器之后 , 把这个pattern设置为安全属性 。 操作方法:
在文件$
JAVA_HOME/conf/security/java.security中增加一行以下内容
jdk.serialFilterFactory=<pattern>

这个过滤器除了可以作为安全属性之外 , 还可以被设置为一个JVM参数 , 格式:
-Djdk.serialFilter=<pattern>

除了安全属性和JVM参数以外 , 还可以在代码中配置 。 调用一个工厂方法 , 传入我们前面创建的pattern , 从而创建出过滤器 。 代码如下:
ObjectInputFilter.Config.createFilter(String);

上面我们使用了JDK自带的过滤器(基于pattern) , 其实通过继承ObjectInputFilter接口 , 可以创建自定义序列化过滤器 。

当然 , 自己实现的序列化过滤器就不再受制于对象图最大深度、对象图最大引用数、对象图最大字节数、最大数组大小、类名/包名和模块名的黑白名单等检查项目了 。