bigywater.tech
Java · 211

ObjectInputFilter:给 Java 反序列化加一道类型边界

怎么给 Java 的反序列化加一道类型边界?这篇只做防御侧的验证:用 ObjectInputFilter 的白名单、黑名单和资源限制,看哪些对象被拒绝、错误是什么、配置在哪一层生效(序列化的字节结构见前面的篇目;不包含任何攻击载荷)。

REJECTED
被过滤器拒绝时的结果:InvalidClassException: filter status: REJECTED
白名单 + !*
推荐的写法:只允许明确列出的类
只能设置一次
进程级过滤器 setSerialFilter 的限制

类型过滤

实测 · 本机执行输出
  不设过滤器:Ok → 成功:Ok;Other → 成功:Other
  白名单过滤器 "Main$Ok;java.lang.*;java.util.ArrayList;!*":Ok → 成功:Ok;Other → InvalidClassException: filter status: REJECTED;ArrayList(含 String)→ 成功:ArrayList
  黑名单过滤器 "!Main$Other":Other → InvalidClassException: filter status: REJECTED;Ok → 成功:Ok(黑名单之外的类默认放行)
  同样的黑名单过滤器,在 JDK 的默认状态(UNDECIDED)下对 ArrayList → 成功:ArrayList

资源限制

实测 · 本机执行输出
  maxdepth=50      嵌套 200 层的数组 → InvalidClassException: filter status: REJECTED
  maxdepth=50      10 万个 int 的数组 → 成功:int[]
  maxdepth=50      含 500 个元素的列表 → 成功:CopiesList
  maxarray=1000    嵌套 200 层的数组 → 成功:Object[]
  maxarray=1000    10 万个 int 的数组 → InvalidClassException: filter status: REJECTED
  maxarray=1000    含 500 个元素的列表 → 成功:CopiesList
  maxrefs=100      嵌套 200 层的数组 → InvalidClassException: filter status: REJECTED
  maxrefs=100      10 万个 int 的数组 → 成功:int[]
  maxrefs=100      含 500 个元素的列表 → InvalidClassException: filter status: REJECTED
  maxbytes=1000    嵌套 200 层的数组 → InvalidClassException: filter status: REJECTED
  maxbytes=1000    10 万个 int 的数组 → 成功:int[]
  maxbytes=1000    含 500 个元素的列表 → InvalidClassException: filter status: REJECTED
  不设限制时这三个都能读:成功:Object[] | 成功:int[] | 成功:CopiesList

设置位置与组合

实测 · 本机执行输出
  设了进程级过滤器 ObjectInputFilter.Config.setSerialFilter("Main$Ok;java.lang.*;!*") 之后,没有给流单独设置过滤器:Ok → 成功:Ok;Other → InvalidClassException: filter status: REJECTED
  给某个流单独设了过滤器 "Main$Other;!*":Other → 成功:Other;Ok → InvalidClassException: filter status: REJECTED(流上的过滤器取代进程级的)
  第二次 setSerialFilter → IllegalStateException: Serial filter can only be set once
  自己写的过滤器(lambda 按类名前缀):Ok → 成功:Ok;ArrayList → InvalidClassException: filter status: REJECTED
  ObjectInputFilter.merge(限制深度 3, 白名单):Ok → 成功:Ok
  rejectUndecidedClass 包装的白名单:InvalidClassException: filter status: REJECTED(只有一个允许项、没有 !* 时,不在清单里的类默认是 UNDECIDED,用它转成拒绝)

分步演示

ObjectInputFilter
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12
    • 没有验证:-Djdk.serialFilter 命令行与 java.security 里的配置、过滤器工厂(ObjectInputFilter.Config.setSerialFilterFactory)、RMI 与 JMX 里内置的过滤器、真实的反序列化漏洞利用链(本篇不演示)、过滤器对性能的影响;maxbytes 拦不住单个大数组的原因没有深究
    京ICP备2026067502号-1京公网安备11010502063487号