velocity 是如何实现内省 屏蔽反射的

本文转自互联网

velocity的标签中支持$abc 这样的语法,如果abc是一个对象,则写模板时就可以利用它来进行反射,调用一些危险的方法,如

1
$vm.getClass().newInstance()
1
# set ($exec  =  "kxlzx" )$exec. class .forName( "java.lang.Runtime" ).getRuntime().exec( "calc" )

通过反射,让系统本身出现了安全漏洞,这类危险的操作,可以通过屏蔽反射来杜绝,在velocity属性中添加一行配置即可

1
runtime.introspector.uberspect  =  org.apache.velocity.util.introspection.SecureUberspector

velocity默认的配置为:

1
runtime.introspector.uberspect  =  org.apache.velocity.util.introspection.UberspectImpl

本文主要讨论从velocity初始化过程到解析标签。以及如何通过

1
SecureUberspector

来屏蔽反射,欢迎补充。

velocity的内省主要的用处是解析如$a.id,$a.name的引用,与其说是内省,不如说是通过反射找get方法。。。。 

 

先来分析velocity的初始化过程

 

wKioL1bX5bCCyQ2zAACy-gC08Zc855.jpg

这里只是对velocity初始化过程的概括,初始化过程大量依赖的配置参数,即velocity.properties,用户一般自定义该文件或直接载入Properties,默认的配置目录为

1
org/apache/velocity/runtime/defaults/velocity.properties

 

调用方法渲染流程

1
2
VelocityEngine velocityEngine  =  new  VelocityEngine( "/velocity.properties" );
velocityEngine.evaluate(context, writer,  "logMsgName" ,   new  InputStreamReader(VelocityTest 2 . class .getResource( "test.vm" ).openStream()));

wKiom1bX5u6yVEdNAAAW1bomJ3E090.jpg

生成NodeTree的代码比较复杂,可能用的是某种算法,总之,最后的Tree里面包含了所有的vm信息,如果是parse\include会生成AsTDirective,如果是文本,会生成ASTText对应,如果是set,会生成ASTSetDirective,如果是引用java教程,会生成ASTReference对应。。等等。。

 

这里列举几个标签的处理流程

 

parse

wKiom1bX5-iA3SdkAAAazLU1Pis057.jpg

 

从源代码分析来看,parse标签里面的内容甚至可以写成动态的,如

1
# parse( "${a}.vm" )

发送includeEvent和velocity初始化过程中的各个事件处理器是对应的,引入vm文件外的文件,都会触发includeEvent,然后根据其返回值,来找到真正的vm资源文件,因此,我们可以在eventHander中重定向返回的资源位置,如 a.vm -> b.vm

另外,parse和include 对velocity来说,是两种type,解析parse文件时,会把context传入进行解析

 

引用标签,如$a,$vm.id

wKiom1bX6ZDCT0KeAABSJIVzYSo775.jpg

普通的引用渲染流程不包括子流程 “SecureUberspector拦截方法”,如果引用值为$a.id ,则会去找a.getid() -> a.getId(),,然后反射调用method.invoke(objecct)

 

引用的渲染流程会根据identifier和method进行不同的流程

wKiom1bX_o3QpXELAAAvoVPfi4M173.jpg

identifier方式即$a.id

method方式即$a.println()

为什么下面两行嗲吗的效果一样呢

1
2
$exec. class
$exec.getClass()

原因就在这里,velocity把class当成一个属性来处理了,因此,去找getClass方法,恰好对象都有getClass方法,这样效果就和直接写$exec.getClass()一样了 

 

SecureUberspector如果达到屏蔽反射方法的呢,先来看一看它的类依赖

wKioL1bX7ITyGCT_AAAmDuthj10561.png

UberspecttImpl中有一个introspector对象,SecureUberspector对其进行了重定义SecureUberspector的初始化方法如下,badPackages和badClass的配置也是在默认的velocity.properties中配置的,用户可以添加更多的配置

1
2
3
4
5
6
7
8
9
10
public  void  init()
     {
         String [] badPackages = runtimeServices.getConfiguration()
                         .getStringArray(RuntimeConstants.INTROSPECTOR_RESTRICT_PACKAGES);
 
         String [] badClasses = runtimeServices.getConfiguration()
                         .getStringArray(RuntimeConstants.INTROSPECTOR_RESTRICT_CLASSES);
         
         introspector =  new  SecureIntrospectorImpl(badClasses, badPackages, log);
     }

 

SecureIntrospectorImpl实现了方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  Method getMethod(Class clazz, String methodName, Object[] params)
         throws  IllegalArgumentException
     {
         if  (!checkObjectExecutePermission(clazz, methodName))
         {
             log.warn( "Cannot retrieve method "  + methodName +
                      " from object of class "  + clazz.getName() +
                      " due to security restrictions." );
             return  null ;
         }
         else
         {
             return  super .getMethod(clazz, methodName, params);
         }
     }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
      * Determine which methods and classes to prevent from executing.  Always blocks
      * methods wait() and notify().  Always allows methods on Number, Boolean, and String.
      * Prohibits method calls on classes related to reflection and system operations.
      * For the complete list, see the properties <code>introspector.restrict.classes</code>
      * and <code>introspector.restrict.packages</code>.
      *
      * @param clazz Class on which method will be called
      * @param methodName Name of method to be called
      * @see org.apache.velocity.util.introspection.SecureIntrospectorControl#checkObjectExecutePermission(java.lang.Class, java.lang.String)
      */
     public  boolean  checkObjectExecutePermission(Class clazz, String methodName)
     {
         /**
          * check for wait and notify
          */
         if  (methodName !=  null  &&
             (methodName.equals( "wait" ) || methodName.equals( "notify" )) )
         {
             return  false ;
         }
 
         /**
          * Always allow the most common classes - Number, Boolean and String
          */
         else  if  (Number. class .isAssignableFrom(clazz))
         {
             return  true ;
         }
         else  if  (Boolean. class .isAssignableFrom(clazz))
         {
             return  true ;
         }
         else  if  (String. class .isAssignableFrom(clazz))
         {
             return  true ;
         }
 
         /**
          * Always allow Class.getName()
          */
         else  if  (Class. class .isAssignableFrom(clazz) &&
                  (methodName !=  null ) && methodName.equals( "getName" ))
         {
             return  true ;
         }
 
         /**
          * check the classname (minus any array info)
          * whether it matches disallowed classes or packages
          */
         String className = clazz.getName();
         if  (className.startsWith( "[L" ) && className.endsWith( ";" ))
         {
             className = className.substring( 2 , className.length() -  1 );
         }
 
         int  dotPos = className.lastIndexOf( '.' );
         String packageName = (dotPos == - 1 ) ?  ""  : className.substring( 0 , dotPos);
 
         for  ( int  i =  0 , size = badPackages.length; i < size; i++)
         {
             if  (packageName.equals(badPackages[i]))
             {
                 return  false ;
             }
         }
 
         for  ( int  i =  0 , size = badClasses.length; i < size; i++)
         {
             if  (className.equals(badClasses[i]))
             {
                 return  false ;
             }
         }
 
         return  true ;
     }

SecureIntrospectorImpl

这里可以看见它对对象访问方法的屏蔽操作

badPackage:java.lang.refect

badClass:

wKiom1bX87zzgQoIAACKXzsDTWM745.jpg

 

那么,为什么我们不直接使用SecureIntrospectorImpl呢,因为它仅仅是一个工具

 

SecureUberspector类对foreach标签也进行了支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
      * Get an iterator from the given object.  Since the superclass method
      * this secure version checks for execute permission.
     
      * @param obj object to iterate over
      * @param i line, column, template info
      * @return Iterator for object
      * @throws Exception
      */
     public  Iterator getIterator(Object obj, Info i)
         throws  Exception
     {
         if  (obj !=  null )
         {
             SecureIntrospectorControl sic = (SecureIntrospectorControl)introspector;
             if  (sic.checkObjectExecutePermission(obj.getClass(),  null ))
             {
                 return  super .getIterator(obj, i);
             }
             else
             {
                 log.warn( "Cannot retrieve iterator from "  + obj.getClass() +
                          " due to security restrictions." );
             }
         }
         return  null ;
     }

就这样,foreach时,如果对象是java.lang.refect包下的类或badClass,就没有权限了

转载于:https://www.cnblogs.com/jinshiyill/p/5240906.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值