OGNL injection

OGNL injection

Description

OGNL (Object-Graph Navigation Language) supports property navigation, method calls, operators, collections and access to objects exposed through an evaluation context. Apache Struts uses OGNL in its value stack and other internal features.

Parsing or evaluating externally controlled text as an OGNL expression may let an attacker access unintended objects or properties. The impact depends on the root object, evaluation context, permitted classes and members, and Struts security settings. It can range from data disclosure or modification to code execution.

The result of parseExpression or compile remains an untrusted expression. Successful parsing does not establish authorization or make later evaluation safe.

Potential impact

  • Data disclosure or modification: An expression may read or change properties in the reachable object graph.
  • Security-logic bypass: If an expression result controls authorization, routing or policy decisions, an attacker may manipulate those decisions.
  • Code execution: Configurations exposing dangerous objects, methods, classes or context access may permit execution with the application’s privileges.
  • Denial of service: Complex or repetitive expressions may consume excessive CPU, memory or object-access resources.

Remediation

Keep expression text under application control and pass external input only as data.

  • Evaluate only fixed, reviewed expressions. Do not concatenate request values into expression text or forced/double-evaluation syntax.
  • If users must select a field or operation, map external keys to constant expressions through a finite server-owned mapping and reject unknown keys.
  • Supply untrusted values as root-object data, evaluation-context data, bindings or property values.
  • Do not assume a regex, escaping, character removal or a helper named sanitize can make arbitrary OGNL expressions safe. Even an alphanumeric-and-underscore restriction can leave an attacker free to choose server-object property names.
  • Keep Apache Struts on a supported release and use the OGNL dependency managed by Struts. The Struts 7.3.0 and OGNL 3.4.12 links below are version-specific references; check support status and security updates when deploying.

Examples

Before

The before example evaluates the entire request value as an OGNL expression.

java
import jakarta.servlet.http.HttpServletRequest;
import ognl.Ognl;
import ognl.OgnlException;

final class VulnerableOgnlExample {
    static Object readField(HttpServletRequest request, UserProfile profile)
            throws OgnlException {
        String requestExpression = request.getParameter("expression");
        return Ognl.getValue(requestExpression, profile);
    }

    static final class UserProfile {
        private final String displayName;
        private final String email;

        UserProfile(String displayName, String email) {
            this.displayName = displayName;
            this.email = email;
        }

        public String getDisplayName() {
            return displayName;
        }

        public String getEmail() {
            return email;
        }
    }
}

requestExpression may contain method calls, context references or other OGNL syntax rather than a simple property name. The attacker chooses the expression itself.

After

The after example treats input as a limited business key and maps it to a server-owned constant expression. It requires a Java version supporting switch expressions. Configure access to the profile and its fields, and error responses, separately.

java
import jakarta.servlet.http.HttpServletRequest;
import ognl.Ognl;
import ognl.OgnlException;

final class SafeOgnlExample {
    static Object readField(HttpServletRequest request, UserProfile profile)
            throws OgnlException {
        String requestedField = request.getParameter("field");
        if (requestedField == null) {
            throw new IllegalArgumentException("Missing field");
        }
        String fixedExpression = switch (requestedField) {
            case "display-name" -> "displayName";
            case "email" -> "email";
            default -> throw new IllegalArgumentException("Unsupported field");
        };

        return Ognl.getValue(fixedExpression, profile);
    }

    static final class UserProfile {
        private final String displayName;
        private final String email;

        UserProfile(String displayName, String email) {
            this.displayName = displayName;
            this.email = email;
        }

        public String getDisplayName() {
            return displayName;
        }

        public String getEmail() {
            return email;
        }
    }
}

Accepts only the two business keys. The string passed to OGNL is always the literal displayName or email defined in code.

Apache Struts defense in depth

These controls supplement fixed-expression design; they do not neutralize attacker-selected expressions.

  • Keep the class/package allow-list struts.allowlist.enable enabled and allow only necessary entries. It is enabled by default in Struts 7.
  • Configure OGNL Guard through struts.ognl.excludedNodeTypes to block unnecessary AST node types.
  • Where possible, disable fallback access to ActionContext with struts.ognl.valueStackFallbackToContext=false.
  • Do not relax Struts 7 restrictions such as struts.ognl.allowStaticFieldAccess=false, struts.disallowProxyObjectAccess=true, struts.disallowDefaultPackageAccess=true and struts.ognl.disallowCustomOgnlMap=true.
  • Keep struts.ognl.expressionMaxLength as low as the application permits. A length limit reduces complexity but does not validate expression meaning.

Apache Struts documents that the -Dognl.security.manager sandbox does not work on JDK 21 or later. Do not rely on it as a security boundary or present it as a solution for current JDKs.

Review expression use

Keep expressions separate from data in custom helpers and templates that evaluate strings later. Review authorization-related results and which root-object data is exposed.

References