1. 简介
在Spring系列产品中,SpEL是表达式计算的基础,实现了与Spring生态系统所有产品无缝对接。Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEL可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。由于它能够在运行时动态分配值,因此可以为我们节省大量Java代码。
spEL表达式有很多特效:
- 使用Bean的ID来引用Bean;
- 可调用方法和访问对象属性
- 可对值进行算数、关系和逻辑运算;
- 可使用正则表达式进行匹配;
- 可进行集合操作;
2. spEL安全漏洞
spEL表达式是可以操作类和方法的,可以通过类型表达式T(Type)来调用任意类方法,这是因为在不指定EvaluationContext
的情况下默认采用StandardEvaluationContext
,而它包含了spEL的所有功能,在允许用户控制输入的情况下可以造成任意命令执行。
public static void main(String[] args) throws Exception {
//表达式。。。
String spel = "T(java.lang.Runtime).getRuntime().maxMemory()";
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(spel);
System.out.println(expression.getValue());
}
例如这个例子,执行用户传入的任意命令,但当用户传入T(java.lang.Runtime).getRuntime().maxMemory()
表达式后便会执行某些方法,存在spEL表达式注入的安全风险。
3. 防御方法
最直接的防御方法就是使用SimpleEvaluationContext
替换StandardEvaluationContext
。
官方文档:SimpleEvaluationContext的API官方文档
private static void test3() {
//执行shell脚本
String spel = "T(java.lang.Runtime).getRuntime().maxMemory()";
ExpressionParser parser = new SpelExpressionParser();
Student student = new Student();
//只读属性
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
//绑定数据
// context.setVariable("student", student);
Expression expression = parser.parseExpression(spel);
System.out.println(expression.getValue(context));
}
SimpleEvaluationContext和StandardEvaluationContext是SpEL提供的两个EvaluationContext:
- SimpleEvaluationContext - 针对不需要SpEL语言语法的全部范围并且应该受到有意限制的表达式类别,公开SpEL语言特性和配置选项的子集。
- StandardEvaluationContext - 公开全套SpEL语言功能和配置选项。您可以使用它来指定默认的根对象并配置每个可用的评估相关策略。
SimpleEvaluationContext旨在仅支持SpEL语言语法的一个子集,不包括 Java类型引用、构造函数和bean引用;而StandardEvaluationContext是支持全部SpEL语法的。
相关文章
spEL—基础语法+注解中动态调用Bean方法
spEL表达式—让注解更加灵活(项目埋点实战)
推荐阅读
【小家Spring】SpEL你感兴趣的实现原理浅析spring-expression~(SpelExpressionParser、EvaluationContext、rootObject)