Powerful expression engine -- OGNL

OGNL (Object Graph Navigation Language) is an open source expression engine. By using OGNL, we can access arbitrary properties in the Java object tree and call methods of the Java object tree through expressions. In other words, if we regard an expression as a string with semantics, OGNL is the catalyst for the communication between the semantic string and Java objects. Through OGNL, we can easily solve various problems encountered in the process of data flow.

/**  
 * Through the incoming OGNL expression, the value is obtained from the root object in a given context 
 */  
public static Object getValue(String expression, Map context, Object root) throws OgnlException {  
  return getValue(expression, context, root, null);  
}  
  
/** 
 * Write a value to the root object in a given context through the incoming OGNL expression 
 */  
public static void setValue(String expression, Map context, Object root, Object value) throws OgnlException {  
  setValue(parseExpression(expression), context, root, value);  
}  

In fact, the OGNL API is quite simple. The above two methods are respectively for the "value" and "write value" operations of the object. Therefore, the basic operation of OGNL is actually realized by passing in the three parameters of the above two methods. OGNL has also written many other methods to achieve the same function. The above two interfaces are only the simplest and most representative methods. Readers can get more information by reading Ognl.java.

<!-- https://mvnrepository.com/artifact/ognl/ognl -->
<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>3.2.21</version>
</dependency>
public class Main {

    public static void main(String[] args) throws OgnlException {

        User user = new User();
        user.setId(1);
        user.setName("downpour");

        // Create context
        Map context = new HashMap();
        context.put("introduction", "My name is ");

        // The test evaluates the expression from the Root object and obtains the result
        Object name = Ognl.getValue(Ognl.parseExpression("name"), user);
        System.out.println(name.toString());

        // The test evaluates the expression from the context and obtains the result
        Object contextValue = Ognl.getValue(Ognl.parseExpression("#introduction"), context, user);
        System.out.println(contextValue);
        // The test evaluates both the Root object and the context as part of the expression
        Object hello = Ognl.getValue(Ognl.parseExpression("#introduction + name"), context, user);
        System.out.println(hello);

        // Write value to Root object
        Ognl.setValue("group.name", user, "dev");
        Ognl.setValue("age", user, "18");

        System.out.println(user.getGroup().getName());

    }
}

OGNL three elements

Expression

Expression is the core of the whole OGNL. All OGNL operations are performed after expression parsing. The expression will specify what the OGNL operation is going to do. Therefore, the expression is actually a string with syntax meaning, which will specify the type and content of the operation.

OGNL supports a large number of expression syntax. It not only supports "chained" description of object access paths, but also supports simple calculations in expressions, and even complex Lambda expressions. We can see a variety of different OGNL expressions in the following chapters.

Root Object

The Root object of OGNL can be understood as the operation object of OGNL. When the OGNL expression specifies "what to do", we also need to specify who to do it to. The Root object of OGNL is actually a Java object, which is the actual carrier of all OGNL operations. This means that if we have an OGNL expression, we actually need to calculate the OGNL expression for the Root object and return the result.

Context

With expressions and Root objects, we can already use the basic functions of OGNL. For example, you can perform value taking or write value operations on the Root object in OGNL according to the expression.

However, in fact, within OGNL, all operations will run in a specific data environment, which is the Context of OGNL. To put it more clearly, this Context will specify where OGNL operations are performed.

The context of OGNL is a Map structure called OgnlContext. In fact, the Root Object we mentioned earlier will also be added to the context and processed as a special variable.

Basic operation of OGNL

Access to Root objects

Access to the object tree of the Root object of OGNL is achieved by concatenating the references of the object with a "point number". In this way, OGNL actually transforms a tree object structure into a chain string structure to express semantics.

// Gets the value of the name property in the Root object  
name       
// Gets the actual value of the name attribute in the department attribute in the Root object  
department.name     
// Gets the actual value of the name attribute in the manager attribute in the department attribute in the Root object  
department.manager.name   

Access to Context

Since the context of OGNL is a Map structure, you can set some parameters in the context environment in advance and let OGNL bring these parameters into the context for calculation. Sometimes it is necessary to access parameters in these contexts. When accessing these parameters, you need to use # symbols and chain expressions to express the difference from accessing Root objects.

// Gets the value of the object named introduction in the OGNL context  
#introduction      
// Gets the value of the property named name in the user object in the object named parameters in the OGNL context  
#parameters.user.name  

Access to static variables

In OGNL, static variables or static methods need to be accessed through the expression syntax of @ [class]@[field / method].

// Access the value of the property named ENABLE in the com.example.core.Resource class  
@com.example.core.Resource@ENABLE      
// Call the method @com.example.core.Resource named get in the com.example.core.Resource class Resource@get ()  

Method call

The method of invocation in OGNL can be done directly by way of Java like method, that is, to complete the method call by adding the name of the dot, and even to pass the parameters.

// Call the size() method of users in the group attribute in the Root object  
group.users.size()      
// Call the containsUser method in the group in the Root object and pass in the value named requestUser in the context as a parameter  
group.containsUser(#requestUser) 

[potential problems caused by OGNL]

*We can already see the power of OGNL at the grammatical level. However, the more powerful things are, there must be fatal weaknesses in themselves, which is the so-called "things will turn when they reach the extreme". It is precisely because OGNL can support the complete process of Java object creation, reading and writing that it can be used as a potential entry point and become the target of hackers.

On July 14, 2010, the exploit DB website revealed a vulnerability of remote execution of arbitrary code in struts 2. The specific declaration link is: http://www.exploit-db.com/exploits/14360/.

Careful readers will find that the basic principle of this vulnerability is to take advantage of the characteristics that OGNL can arbitrarily construct objects and execute methods in objects, construct a Java class called by underlying commands, and execute operating system commands to carry out system attacks.

In versions after struts 2.2. X, this vulnerability is fixed. The main method is to reject similar code execution by limiting parameter names*

https://developer.aliyun.com/article/135737

Posted by QSDragon on Sat, 04 Dec 2021 20:07:03 -0800