JAVA代码审计之SpEL表达式
字数 1660 2025-08-12 11:34:30
Spring Expression Language (SpEL) 表达式注入漏洞分析与防御
1. SpEL 基础
Spring Expression Language (SpEL) 是一种功能强大的表达式语言,用于在运行时查询和操作对象图。语法上类似于 Unified EL,但提供了更多特性,特别是方法调用和基本字符串模板函数。
1.1 环境配置
在 Maven 项目中引入 SpEL 依赖:
<properties>
<org.springframework.version>5.0.8.RELEASE</org.springframework.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
1.2 SpEL 使用流程
SpEL 表达式求值一般分为四步:
- 构造解析器:使用
ExpressionParser接口表示解析器,SpelExpressionParser是默认实现 - 解析表达式:使用
parseExpression方法解析字符串表达式为Expression对象 - 构造上下文(可选):准备变量定义等上下文数据
- 求值:通过
getValue方法根据上下文获得表达式值
示例代码:
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("('Hello' + ' lisa').concat(#end)");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("end", "!");
System.out.println(expression.getValue(context));
2. SpEL 的三种主要用法
2.1 注解方式
@Value("#{表达式}")
public String arg;
2.2 XML 配置方式
<bean id="Bean1" class="com.test.xxx">
<property name="arg" value="#{表达式}">
</bean>
2.3 代码动态处理方式
@RequestMapping("/spel")
public String spel(@RequestParam(name = "spel") String spel) {
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression(spel);
Object object = expression.getValue();
return object.toString();
}
3. SpEL 语法特性
SpEL 使用 #{...} 作为定界符,支持以下操作:
- 引用其他对象:
#{car} - 引用其他对象的属性:
#{car.brand} - 方法调用:
#{car.toString()} - 属性引用也可用
$符号:${someProperty}
3.1 类类型表达式
使用 T(Type) 表示 java.lang.Class 实例,"Type"必须是类全限定名(java.lang 包除外):
#{T(java.lang.Math)}
可以访问类静态方法和静态字段:
#{T(java.lang.Runtime).getRuntime().exec('calc')}
4. SpEL 表达式注入漏洞
4.1 漏洞产生条件
- 传入的表达式未经过滤
- 表达式解析后调用了
getValue或setValue方法 - 使用
StandardEvaluationContext(默认)作为上下文对象
4.2 漏洞示例
@GetMapping("/spel/vuln")
public String rce(String expression) {
ExpressionParser parser = new SpelExpressionParser();
Expression expression1 = parser.parseExpression(expression);
Object obje = expression1.getValue();
String obj_str = obje.toString();
return obj_str;
}
攻击者可以构造恶意表达式实现 RCE:
http://localhost:8080/spel/vuln?expression=T(java.lang.Runtime).getRuntime().exec('calc')
5. 漏洞审计方法
- 全局搜索
org.springframework.expression.spel.standard - 搜索
expression.getValue()或expression.setValue() - 定位到具体漏洞代码后,分析传入参数是否可控
- 追踪参数来源,确认用户输入是否未经处理直接传入
6. 修复建议
6.1 使用 SimpleEvaluationContext
SimpleEvaluationContext 仅支持 SpEL 语言语法的一个子集,不包括 Java 类型引用、构造函数和 bean 引用:
@GetMapping("/spel/sec")
public String spel_sec(String expression) {
ExpressionParser parser = new SpelExpressionParser();
// 只读属性
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
return parser.parseExpression(expression).getValue(context).toString();
}
6.2 两种 EvaluationContext 对比
| 特性 | SimpleEvaluationContext | StandardEvaluationContext |
|---|---|---|
| Java 类型引用 | 不支持 | 支持 |
| 构造函数 | 不支持 | 支持 |
| Bean 引用 | 不支持 | 支持 |
| 安全性 | 高 | 低 |
| 功能 | 受限 | 完整 |
7. 关键词总结
SpelExpressionParser:SpEL 表达式解析器StandardEvaluationContext:完整功能的上下文(不安全)SimpleEvaluationContext:受限功能的上下文(安全)T():类类型运算符(可能导致 RCE)getValue()/setValue():表达式求值方法
8. 已知相关漏洞
- Jackson 的 CVE-2017-17485
- WebLogic 的 CVE-2019-2725
这些漏洞都是通过反序列化让程序加载恶意构造的 XML 文件,利用 SpEL 表达式实现远程代码执行。