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


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

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

      

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

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

      

    velocity默认的配置为:

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

      

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

    SecureUberspector
    

      

    来屏蔽反射,欢迎补充。

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

    关于内省的概念 传送门:http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html

    先来分析velocity的初始化过程

    wKioL1bX5bCCyQ2zAACy-gC08Zc855.jpg

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

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

      

    调用方法渲染流程

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

      

    wKiom1bX5u6yVEdNAAAW1bomJ3E090.jpg

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

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

    parse

    wKiom1bX5-iA3SdkAAAazLU1Pis057.jpg

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

    #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()

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

    $exec.class
    $exec.getClass()
    

      

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

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

    wKioL1bX7ITyGCT_AAAmDuthj10561.png

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

    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实现了方法

    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);
            }
        }
    /**
         * 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标签也进行了支持

    /**
         * 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,就没有权限了

  • 相关阅读:
    Java运算符>、>>、>>>三者的区别
    深入浅析react native es6语法
    javascript基础学习(十五)
    javascript基础学习(十四)
    javascript基础学习(十三)
    javascript基础学习(十二)
    javascript基础学习(十一)
    javascript基础学习(十)
    javascript基础学习(九)
    javascript基础学习(八)
  • 原文地址:https://www.cnblogs.com/windliu/p/6376847.html
Copyright © 2020-2023  润新知