专栏名称: 天融信阿尔法实验室
天融信阿尔法实验室将不定期推出技术研究新方向成果,专注安全攻防最前沿技术
目录
相关文章推荐
知产库  ·  美摄vs抖音8案判赔8000万+ ·  昨天  
知产宝  ·  合同纠纷 | ... ·  3 天前  
51好读  ›  专栏  ›  天融信阿尔法实验室

SPEL表达式注入漏洞深入分析

天融信阿尔法实验室  · 公众号  ·  · 2021-09-01 16:41

正文

0x00 SPEL简介



SPEL(Spring Expression Language),即Spring表达式语言,是比JSP的EL更强大的一种表达式语言。从Spring 3开始引入了Spring表达式语言,它能够以一种强大而简洁的方式将值装配到Bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到值。使用SPEL你可以实现超乎想象的装配效果,这是其他装配技术很难做到的。


0x01  SPEL使用



SPEL的使用可以分为两种方式,第一种是在注解中进行使用,另一种是通过SPEL组件提供的接口来进行解析。

首先是通过注解来使用

//@Value能修饰成员变量和方法形参//#{}内就是SpEL表达式的语法//Spring会根据SpEL表达式语法,为变量arg赋值@Value("#{表达式}")public String arg;
//将"hello"字符串赋值给word变量@Value("hello")private String word;
//从网址"http://www.baidu.com"获取资源@Value("http://www.baidu.com")private Resource url;


接下来是通过调用接口的使用情况

   public static void main(String[] args) {     //实例化表达式解析对象        ExpressionParser parser = new SpelExpressionParser();     //调用该对象的parseExpression方法来执行表达式        Expression expres = parser.parseExpression("3*3");     //获取表达式的执行结果,想要返回的结果类型可以以这种Type.class的形式传入        String message = expres.getValue(String.class);        System.out.println(message);

这段代码是执行一段简单的SPEL表达式“3*3”,最终执行结果如下所示

SPEL表达式还能执行一些更复杂的命令,例如对一个对象进行操作,代码如下所示,首先是一个pojo类

public class User {    public String userName;    public User() {    }    public User(String userName) {        this.userName = userName;    }    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;    }    public String sayHi(String name){        return name+" say: Hi";    }      public static String sayBye(String userName){        return userName+"say: Bye";    }}

然后是通过Spel表达式操作user对象的属性

User user = new User();//实例化表达式解析对象ExpressionParser parser = new SpelExpressionParser();//实例化上下文,将user对象作为参数传入,这样就可以操作user对象的属性了StandardEvaluationContext context = new StandardEvaluationContext(user);/**如果不想在实例化上下文的时候就传入对象的话就可以使用下面的代码进行等价替换
StandardEvaluationContext context = new StandardEvaluationContext();context.setRootObject(user);
之所以可以这么替换是因为StandardEvaluationContext在构造方法中还是通过调用了setRootObject方法通过setRootObject方法传入的参数会被放入StandardEvaluationContext.rootObject属性中
*/
//向上下文中添加元素context.setVariable("newUserName","Jone");//这里的userName就是user.userName属性,#newUserName就是上一步中添加的,newUserName为key,而value为Jone,所以这一步是将newUserName的值赋值给user.userName属性parser.parseExpression("userName=#newUserName").getValue(context);System.out.println(user.getUserName());
//这一步是通过Spel表达式直接给user.userName属性赋值parser.parseExpression("userName='Tom'").getValue(context);System.out.println(user.getUserName());
//通过setVariable传入上下文中的参数会被放入StandardEvaluationContext.variables属性中,该属性为HashMap类型,传入的字符串“user”,就是他的key值,value就是user这个对象context.setVariable("user",user);
//通过setVariable方法存放入上下文中的对象,就可以通过 #+key+属性的方式进行调用String name = (String)parser.parseExpression("#user.userName").getValue(context);//通过setVariable方法传入的对象和通过setRootObject方法传入的对象是不一样的,通过setRootObject传入的对象可以直接通过“属性名称”来进行调用,而通过setVariable方法传入的对象,只能通过“#+key+属性的方式进行调用”

可以操作对象的属性SPEL同样也可以操作对象的方法,例如我们的pojo类User中就有一个成员方法sayHi,和一个静态方法sayBye,我们使用SPEL表达式来分别调用一下


首先是调用成员方法,也就是动态方法

User user = new User();ExpressionParser parser = new SpelExpressionParser();StandardEvaluationContext context = new StandardEvaluationContext(user);context.setVariable("user",user);//如下可以使用 #+Key+MethodName的形式进行调用//这种方法不仅可以调用动态方法,也可以调用静态方法String result = (String) parser.parseExpression("#user.sayHi('jack')").getValue(context);System.out.println(result);

运行结果如下

然后是调用静态方法,代码如下所示

ExpressionParser parser = new SpelExpressionParser();//使用“T(Type)”来表示java.lang.Class类的实例,即如同java代码中直接写类名。此方法一般用来引用常量或静态方法String result = parser.parseExpression("T(com.Spel.pojo.User).sayBye('Jack')").getValue(String.class);
System.out.println(result);

除了以上两种方可以调用静态方法以外还有一种方法

ExpressionParser parser = new SpelExpressionParser();StandardEvaluationContext context = new StandardEvaluationContext();//通过反射拿到User类的sayBye方法对象,Method sayBye = User.class.getMethod("sayBye"






请到「今天看啥」查看全文