Java Code Auditing Fundamentals: The Java Reflection Mechanism

Summary0x01 What Is Reflection? Reflection is a Java feature absent from C/C++. It lets a running program inspect itself and operate on internal properties of classes and objects. Oracle explains: 'Reflection enables Java code to disc…'

Code AuditingjavaJava Code AuditingJava reflectionReflection

0x01 What Is Reflection?

Reflection is a Java feature absent from C/C++. It lets a running Java program inspect itself and operate on internal class or object properties. What exactly is reflection?

Oracle describes it as follows:

“Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.”

Simply put, reflection exposes the members and metadata of every runtime type. In Java, it can determine an object's class, discover any class's fields and methods, and invoke any object's methods or access its fields. This dynamic inspection and invocation is Java reflection.

0x02 Uses of Reflection

Reflection is widely used. IDEs such as Eclipse and IntelliJ use it to list properties and methods after an object or class is entered. JavaBeans and JSP also use it. Its most important use, however, is building general frameworks such as Spring and ORM frameworks.

Reflection's importance depends on the role. It is small but essential for framework developers because it underpins containers. Ordinary application developers use it less unless working deeply with frameworks. Understanding framework internals still benefits programming design.

0x03 Basic Uses of Reflection

Because most Java application frameworks rely on reflection, mastering it materially improves code-auditing ability.

1. Obtain a Class Object

There are many ways to obtain a class object; here are four:

  • First: useforName() method

To use methods on Class, callforName()method. It needs only the class name, making it convenient and extensible. Example:

Figure 4-13.png
Figure 4-13.png

This is familiar from JDBC configuration, where it is commonly written as:

Figure 4-14.png
Figure 4-14.png
  • Second: obtain directly

Every data type has a static property accessible through.classto obtain the corresponding Class object. It is simple but still requires a known static member:

Figure 4-15.png
Figure 4-15.png
  • Third: usegetClass() method

Through Object's getClass() method to obtain the bytecode object, but this is cumbersome because the concrete class must be known and instantiated:

Figure 4-16.png
Figure 4-16.png
  • Fourth: use getSystemClassLoader().loadClass() method

getSystemClassLoader().loadClass() method and forName() is similar: only the class name is needed, but unlikeforName()method differs somewhat fromforName()static method causes the JVM to load the class and execute static { } code in, while getSystemClassLoader().loadClass() does not execute static() code. JDBC commonly uses forName()asks the JVM to locate and load a named class. Here, pass "com.mysql.jdbc.Driver" as the argument tells the JVM to look under "com.mysql.jdbc" tells the JVM to locate Driver at that path and load it. The method is shown below:

Figure 4-17.png
Figure 4-17.png

2. Retrieve Class Methods

The principal methods for obtaining Class objects are:

  • First method:getDeclaredMethods()method

getDeclaredMethods() returns every method declared by a class or interface—public, protected, private, and package-private—but excludes inherited methods:

Figure 4-18.png
Figure 4-18.png

Second method:getMethods() method

getMethods() returns every public method of a class, including inherited public methods:

Figure 4-19.png
Figure 4-19.png
  • Third method:getMethod()method

getMethod() returns one specific method, such as Runtime'sexec()method. The first argument is the method name; later arguments are Class objects representing parameter types:

Figure 4-20.png
Figure 4-20.png
  • Fourth method:getDeclaredMethod()method

getDeclaredMethod()method andgetMethod()is similar and returns only one specific method. Its first argument is the method name; the second supplies parameter types:

Figure 4-21.png
Figure 4-21.png

3. Retrieve Class Fields

To demonstrate field-retrieval methods clearly, first create a Student class:

Figure 4-22.png
Figure 4-22.png

To retrieve fields from Student, use one of these methods:

  • First method:getDeclaredFields()method

getDeclaredFields()returns an array of declared fields—public, private, and protected—but excludes inherited fields:

Figure 4-23.png
Figure 4-23.png
  • Second method:getFields()method

getFields()returns all public fields, including inherited fields:

Figure 4-24.png
Figure 4-24.png
  • Third method:getDeclaredField()method

This method is similar togetDeclaredFields()differs in that it returns one field only. To retrieve Student.name:

Figure 4-25.png
Figure 4-25.png
  • Fourth method:getField()method

andgetFields()is similar,getField()returns a particular public field, including inherited fields. To retrieve Student's public content field:

Figure 4-26.png
Figure 4-26.png

0x04 Unsafe Reflection

As discussed above, Java reflection can ignore access modifiers, invoke arbitrary class methods, and read or modify fields. This can create security problems: if an attacker creates an unexpected control-flow path, they may bypass checks. Consider this code:

JAVA
  String name = request.getParameter("name");
  Command command = null;
   if (name.equals("Delect")) {
     command = new DelectCommand();
  } else if (ctl.equals("Add")) {
     command = new AddCommand();
  } else {
   ...
  }
  command.doAction(request);

contains a name field. It checks the requested name: Delect invokes DelectCommand, Add invokes AddCommand, and other values execute other code.

Suppose a developer sees the code and refactors it with reflection to reduce lines:

JAVA
String name = request.getParameter("name");
  Class ComandClass = Class.forName(name + "Command");
  Command command = (Command) CommandClass.newInstance();
  command.doAction(request);

The refactoring reduces code and removes if/else while allowing new commands without changing the dispatcher. But without restricting name, it can instantiate any Command implementation. An attacker may not even be limited to Command: arbitrary classes and default constructors could be invoked, including Runtime for operating-system commands. Unsafe reflection can therefore lead to severe remote code execution and deserves close audit attention.

0x05 Closing Notes

Much more detailed material on Java reflection is available online. It is worth studying and will help auditing skills.