经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
OGNL表达式注入分析
来源:cnblogs  作者:F12~  时间:2024/3/29 8:45:42  对本文有异议

OGNL基础

依赖

  1. <dependency>
  2. <groupId>ognl</groupId>
  3. <artifactId>ognl</artifactId>
  4. <version>3.1.19</version>
  5. </dependency>

OGNL三要素

  • Expression表达式
  • root根对象、即操作对象
  • context上下文,用于保存对象运行的属性及值,有点类似运行环境的意思,保存了环境变量

看个例子

  1. package org.example;
  2. public class Tester {
  3. public User user;
  4. public User getUser() {
  5. return user;
  6. }
  7. public void setUser(User user) {
  8. this.user = user;
  9. }
  10. }
  1. package org.example;
  2. public class User {
  3. private String name;
  4. private int age;
  5. public String getName(){
  6. return name;
  7. }
  8. public int getAge(){
  9. return age;
  10. }
  11. public void setName(String name){
  12. this.name = name;
  13. }
  14. public void setAge(int age){
  15. this.age = age;
  16. }
  17. public User(String name, int age){
  18. this.name = name;
  19. this.age = age;
  20. }
  21. }
  1. package org.example;
  2. import ognl.Ognl;
  3. import ognl.OgnlContext;
  4. import ognl.OgnlException;
  5. public class Main {
  6. public static void main(String[] args) throws OgnlException {
  7. Tester tester = new Tester();
  8. User user = new User("F12", 20);
  9. tester.setUser(user);
  10. // 创建context, 设置root
  11. OgnlContext context = new OgnlContext();
  12. context.setRoot(tester);
  13. // 设置表达式
  14. String expression = "user.name";
  15. // 解析表达式
  16. Object ognl = Ognl.parseExpression(expression);
  17. // 调用获取值
  18. Object value = Ognl.getValue(ognl, context, context.getRoot());
  19. System.out.println(value);
  20. }
  21. }
  22. // 输出
  23. F12

运行以上代码就是获取org.example.Tester.user.name的值,上述我们是创建了一个tester,并且让他的user属性为一个User对象,且tester设置为root,表达式为user.name也就是获取root即tester的user属性的name属性。

OGNL语法

  • .操作符

一个例子,(#a=new java.lang.String("calc")).(@java.lang.Runtime@getRuntime().exec(#a)),也可以这样(#a=new java.lang.String("calc")),(@java.lang.Runtime@getRuntime().exec(#a)),中间的点换成逗号。可以发现它执行的方式有点类似递归,他把.前面的表达式当做结果给后面的表达式执行了这里需要注意一下#前我们用括号包裹起来了,这是为了符合语法,假如去掉那一层包裹会报错

  • #操作符

用于调用非root对象

  1. package org.example;
  2. import ognl.Ognl;
  3. import ognl.OgnlContext;
  4. import ognl.OgnlException;
  5. public class Main {
  6. public static void main(String[] args) throws OgnlException {
  7. Tester tester = new Tester();
  8. User user = new User("F12", 20);
  9. tester.setUser(user);
  10. // 创建context, 设置root
  11. OgnlContext context = new OgnlContext();
  12. // context.setRoot(tester);
  13. context.put("user", user);
  14. // 设置表达式
  15. String expression = "#user.name";
  16. // 解析表达式
  17. Object ognl = Ognl.parseExpression(expression);
  18. // 调用获取值
  19. Object value = Ognl.getValue(ognl, context, context.getRoot());
  20. System.out.println(value);
  21. }
  22. }
  23. // 输出
  24. F12

用于创建Map
#{"name": "f12", "level": "noob"}
用于定义变量
如一开始的例子#a=new java.lang.String("calc"),定义了一个字符串常量

  • @操作符

用于调用静态属性、静态方法、静态变量,如上述的@java.lang.Runtime@getRuntime().exec

OGNL版本限制

在OGNL>=3.1.25版本中设置了黑名单

  1. public static Object invokeMethod(Object target, Method method, Object[] argsArray)
  2. throws InvocationTargetException, IllegalAccessException
  3. {
  4. if (_useStricterInvocation) {
  5. final Class methodDeclaringClass = method.getDeclaringClass(); // Note: synchronized(method) call below will already NPE, so no null check.
  6. if ( (AO_SETACCESSIBLE_REF != null && AO_SETACCESSIBLE_REF.equals(method)) ||
  7. (AO_SETACCESSIBLE_ARR_REF != null && AO_SETACCESSIBLE_ARR_REF.equals(method)) ||
  8. (SYS_EXIT_REF != null && SYS_EXIT_REF.equals(method)) ||
  9. (SYS_CONSOLE_REF != null && SYS_CONSOLE_REF.equals(method)) ||
  10. AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) ||
  11. ClassResolver.class.isAssignableFrom(methodDeclaringClass) ||
  12. MethodAccessor.class.isAssignableFrom(methodDeclaringClass) ||
  13. MemberAccess.class.isAssignableFrom(methodDeclaringClass) ||
  14. OgnlContext.class.isAssignableFrom(methodDeclaringClass) ||
  15. Runtime.class.isAssignableFrom(methodDeclaringClass) ||
  16. ClassLoader.class.isAssignableFrom(methodDeclaringClass) ||
  17. ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) ||
  18. AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass) ) {
  19. throw new IllegalAccessException("........");
  20. }

投影与选择

OGNL 支持类似数据库当中的选择与投影功能。

  • 投影:选出集合当中的相同属性组合成一个新的集合。语法为 collection.{XXX},XXX 就是集合中每个元素的公共属性。
  • 选择:选择就是选择出集合当中符合条件的元素组合成新的集合。语法为 collection.{Y XXX},其中 Y 是一个选择操作符,XXX 是选择用的逻辑表达式。选择操作符有 3 种:
    • ? :选择满足条件的所有元素
    • ^:选择满足条件的第一个元素
    • $:选择满足条件的最后一个元素
  1. User p1 = new User("name1", 11);
  2. User p2 = new User("name2", 22);
  3. User p3 = new User("name3", 33);
  4. User p4 = new User("name4", 44);
  5. Map<String, Object> context = new HashMap<String, Object>();
  6. ArrayList<User> list = new ArrayList<User>();
  7. list.add(p1);
  8. list.add(p2);
  9. list.add(p3);
  10. list.add(p4);
  11. context.put("list", list);
  12. System.out.println(Ognl.getValue("#list.{age}", context, list));
  13. // [11, 22, 33, 44]
  14. System.out.println(Ognl.getValue("#list.{age + '-' + name}", context, list));
  15. // [11-name1, 22-name2, 33-name3, 44-name4]
  16. System.out.println(Ognl.getValue("#list.{? #this.age > 22}", context, list));
  17. // [org.example.User@6433a2, org.example.User@5910e440]
  18. System.out.println(Ognl.getValue("#list.{^ #this.age > 22}", context, list));
  19. // [org.example.User@6433a2]
  20. System.out.println(Ognl.getValue("#list.{$ #this.age > 22}", context, list));
  21. // [org.example.User@5910e440]

OGNL Expression解析流程

getValue处打个断点,跟进,注意这个node的类型ASTchain在OGNL表达式中,解析和执行就是通过ASTXXXX这些方法去解析执行的,一共有ASTChainASTConstASTCtorASTInstanceofASTListASTMethodASTStaticFieldASTStaticMethod.....等多种方法,其中最根本的就是chain

进入chain的getValue方法

进入evaluateGetValueBody方法,这里判断context是不是const,这里并不是

往下走进入getValueBody,获取子节点,并进入子节点的getValue方法,然后就这样一直循环

最后进入OgnlRuntime.callMethod

一直往下走,这里invoke,弹出计算器

原文链接:https://www.cnblogs.com/F12-blog/p/18102189

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号