JNDI-ObjectFactory
我们这里讨论 remote codebase
1) JNDI 里“远程 codebase”到底指什么
当目录(LDAP/RMI/COS 等)返回的是一个 Reference 时,它不仅能带 工厂类名,还可以带 工厂类的位置(factoryLocation),而且这个位置可以是一串 URL,用于“去这些 URL 加载工厂类定义”。Reference 的文档就明确说了它包含 factory 的 class name 和 location,并且 location 是用于加载该 factory 的 URL 列表。
这就是所谓 remote codebase:把“要执行的构造逻辑(工厂类)”的字节码来源指到远端。
2) JDK 做的限制:只关掉“远程加载类”,不关掉“使用工厂”
JDK 8u121 的发布说明里写得很直白:“通过存储在命名/目录服务中的 JNDI object factories 进行远程类加载,默认禁用”;如要启用,需要显式把 com.sun.jndi.rmi.object.trustURLCodebase / com.sun.jndi.cosnaming.object.trustURLCodebase 设为 "true"。
注意这里禁用的是 remote class loading(从 URL codebase 拉类),不是禁用 ObjectFactory 机制本身。
3) 为什么 ObjectFactory “不受影响”
因为 ObjectFactory 的核心价值就是:JNDI 查到的东西很多时候是“描述/引用”,真正可用对象要靠工厂去“组装”。ObjectFactory#getObjectInstance() 的定义就是“基于 location 或 reference 信息创建对象”,而 NamingManager.getObjectInstance() 会依次加载并调用工厂,直到拿到非空对象。
如果 JDK 把 “ObjectFactory 一律不执行”:
- 企业 Java 里大量合法场景会直接瘫痪(JNDI DataSource、JMS 资源、应用服务器对象代理等都依赖这种还原流程)
- JNDI 就只剩“查名字返回一坨原始数据”,失去“名字→对象”的意义
所以 JDK 的思路是 最小破坏式修补:
禁掉最危险、最不该默认存在的能力:从不可信远端下载字节码; 保留设计上必须存在、且大量业务依赖的能力:用本地 classpath 里的工厂还原对象。
换句话说:remote codebase 是“把新代码引进来”;而 ObjectFactory(本地)是“用已有代码按说明书造对象”。JDK 只禁止前者。
4) 这也解释了“为什么还能被绕过”
当你看到“绕过只禁 remote codebase 的 JNDI 注入”,通常是因为攻击不再依赖“下载新类”,而是:
- 让 JNDI 去调用 目标进程 classpath 里已经存在的某个工厂/构造路径(JDK 不可能因为安全就把这些合法能力全砍掉)
为此,JDK 后来也在 LDAP 场景加了更细的控制:例如 jdk.jndi.ldap.object.factoriesFilter 用来限制 哪些 ObjectFactory 类允许从 LDAP 返回的引用中被实例化,并给了默认的 allowlist/denylist 形式配置。
另外还加强了 LDAP 里对 Java 对象反序列化的默认行为(例如 com.sun.jndi.ldap.object.trustSerialData 相关默认值与控制)。
5) 你可以用一个“心智模型”来记
- 远程 codebase 限制:不让目录服务“顺便带来一段新代码”。(JDK 8u121 起对 RMI/COS 默认关)
- ObjectFactory 机制保留:JNDI 仍要把
Reference变成对象;只要工厂在本地 classpath 里,就会照常走getObjectInstance()。