双亲委派机制防止用户自定义的java.lang.*等核心类被加载,因bootstrap类加载器独占加载权且jvm校验包名;实战中需防范绕过委派的伪核心类攻击,并确保加载器链合规。

双亲委派机制本身不直接“防止核心变量对象被恶意替换”,它防止的是同名核心类(如 java.lang.String、java.lang.Object)被用户自定义版本加载并执行。所谓“核心变量对象”如果指这些类的实例,其安全性依赖于类本身的加载来源是否可信——而双亲委派正是保障这一点的关键机制。实战中,你不需要主动“利用”它来编码防护,而是要理解它如何工作、哪些行为会绕过它、以及如何避免无意破坏它带来的保护。
核心原理:谁加载了 java.lang.*,谁就掌控了它的语义
Java 规定所有以 java. 开头的包(如 java.lang、java.util、java.io)只能由 Bootstrap 类加载器加载。该加载器用 C++ 实现,只从 $JAVA_HOME/jre/lib/rt.jar(或模块化后的 java.base)中读取字节码,且不接受 Java 层配置或替换。
这意味着:
- 即使你在项目里写一个
public class java.lang.String { ... },编译能通过,但运行时绝不会被加载——因为 AppClassLoader 收到请求后,会一路委派给 Bootstrap;Bootstrap 在 rt.jar 中找到官方 String,直接加载并返回,子加载器根本没机会执行自己的 findClass。 - 你无法通过 ClassLoader.defineClass() 手动注入一个 java.lang.String 的 Class 对象,JVM 在 defineClass 阶段会对包名做硬性校验,非启动类加载器定义 java.* 类会直接抛 SecurityException。
实战中真正要防的,是绕过委派的“伪核心类”陷阱
攻击者通常不硬刚 java.lang.*,而是制造“看起来像核心、实则可控”的类。比如:
- 在自定义类加载器中,把恶意
com.example.StringUtils放进高优先级路径,再诱导框架通过反射加载它; - 利用线程上下文类加载器(TCCL)打破双亲委派,在 JNDI、JDBC、XML 解析等场景中,让第三方库误用你提供的加载器去加载关键服务接口实现;
- 篡改 classpath,把伪造的 commons-lang3.jar(含被污染的 org.apache.commons.lang3.StringUtils)放在系统类路径最前面,虽仍由 AppClassLoader 加载,但因它本就不受双亲委派保护(非 java.*),就会生效。
应对方法不是改委派逻辑,而是:
- 校验关键工具类的
getClass().getClassLoader(),确保它来自 AppClassLoader 或 Extension,而非不可信的自定义加载器; - 禁用 TCCL 在敏感流程中的传播,例如在 ServletFilter 或 Spring Bean 初始化时显式设置
Thread.currentThread().setContextClassLoader(null); - 使用模块化(Java 9+)或 JEP 261 的封装策略,通过
--illegal-access=deny和opens/exports精确控制包可见性,从源头限制反射篡改。
不要试图“加强”双亲委派,而要守住它的前提条件
双亲委派有效,依赖两个隐含前提:
-
Bootstrap 加载器的加载路径未被污染:不要手动修改
sun.boot.class.path或替换 rt.jar;使用 JDK 官方发行版,避免来路不明的定制 JRE。 -
应用类加载器未被错误地设为 Bootstrap 的子类:自定义加载器若调用
super(null)指定父为 null,就等于把自己变成“新 Bootstrap”,可能加载非法 java.* 类(JVM 会拦截,但已暴露风险);应始终用super(ClassLoader.getSystemClassLoader())继承标准链。
检查当前环境是否合规,可用一行代码验证:
System.out.println(String.class.getClassLoader()); // 输出 null 表示由 Bootstrap 加载,正常替代方案:用运行时约束补位,而非依赖加载时机
对于真正需要保护的“核心变量对象”(比如单例配置管理器、加密密钥容器),不要寄望类加载机制,而应结合语言层与 JVM 层防护:
- 将敏感类声明为
final,禁止继承篡改; - 用
SecurityManager(已弃用但部分老系统仍在用)或java.lang.RuntimePermission("createSecurityManager")配合策略文件,限制defineClass、accessDeclaredMembers等危险操作; - 在初始化阶段对关键对象做
System.identityHashCode()快照,后续定期校验,发现哈希变化即告警——这能捕获运行时通过 Unsafe 或反射做的对象篡改。










