Skip to main content

Overview

Expression Language (EL) Injection occurs when an application incorporates untrusted user input into strings that are later interpreted by an Expression Language engine. ELs are often used in Java EE frameworks (like JSF, JSP), Spring Expression Language (SPeL), and some templating systems to provide dynamic functionality. If user input containing EL syntax (e.g., ${...}, #{...}, *{...}) is evaluated, attackers can execute arbitrary code, access sensitive objects, or manipulate application behavior. ⚙️💉

Business Impact

Successful EL Injection can lead to:
  • Remote Code Execution (RCE): Attackers can often access underlying platform objects and methods, potentially executing arbitrary commands on the server.
  • Information Disclosure: Accessing sensitive application objects, configuration settings, or environment variables.
  • Logic Manipulation: Altering application flow or data by triggering unexpected method calls.
  • Denial of Service: Executing expressions that consume excessive resources.

Reference Details

CWE ID: CWE-917 OWASP Top 10 (2021): A03:2021 - Injection Severity: Critical (often leads to RCE)

Framework-Specific Analysis and Remediation

This vulnerability is highly dependent on the specific framework and how it parses and evaluates expressions. Key defenses include:
  1. Avoiding Evaluation of User Input: The safest approach is never to include untrusted data in strings that will be evaluated by an EL engine.
  2. Using Safe EL Subsets/Contexts: Some frameworks allow configuring restricted EL contexts that limit access to dangerous objects or methods.
  3. Input Sanitization/Escaping: Escaping EL control characters ($, #, {, }) in user input before it’s processed by the EL engine. This is less reliable than avoiding evaluation.
  4. Keeping Frameworks Updated: Patches often fix EL injection vulnerabilities discovered in framework components.

  • Java
  • Python
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Common in Java EE (JSF, JSP using ${} or #{}) and Spring applications using Spring Expression Language (SPeL - #{}, *{}). User input might end up in dynamically generated error messages, custom components, or unsafe template processing.

Vulnerable Scenario 1: JSF/JSP Error Message

An error message reflects user input without sanitization, and the page renders EL.
// Backing Bean (JSF)
FacesContext context = FacesContext.getCurrentInstance();
String userInput = request.getParameter("searchTerm");
// DANGEROUS: User input included directly in a message shown on a page that evaluates EL.
// Input: searchTerm = ${7*7} -> Message becomes: Search failed for 49
// Input: searchTerm = ${customer.resetPassword('newPass')} -> Calls method
// Input: searchTerm = ${request.getParameter('cmd')} -> Might echo command
// Input: searchTerm = #{request.getSession().invalidate()} -> Logs out user
// Input: searchTerm = ${facesContext.getExternalContext().getResponse().setHeader('X-Evil','Injected')}
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
    "Search failed for " + userInput, null));
<h:messages errorClass="errorMessage" />

Vulnerable Scenario 2: Spring Expression Language (SPeL) in Custom Annotation/Logic

A developer uses SpelExpressionParser to evaluate an expression partly constructed from user input.
// service/DynamicRuleService.java
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Service;

@Service
public class DynamicRuleService {
    public boolean evaluateRule(String userInput, Object context) {
        ExpressionParser parser = new SpelExpressionParser();
        // DANGEROUS: User input concatenated into the expression string.
        // Input: userInput = "') OR T(java.lang.Runtime).getRuntime().exec('calc.exe').toString().contains('". Pad if needed.
        // Expression becomes: "#root.value > 10 AND ('') OR T(java.lang.Runtime)..."
        String expressionString = "#root.value > 10 AND ('" + userInput + "' != '')";
        try {
            Expression exp = parser.parseExpression(expressionString);
            return exp.getValue(context, Boolean.class);
        } catch (Exception e) {
             System.err.println("SPeL Error: " + e.getMessage());
             return false;
        }
    }
}

Mitigation and Best Practices

  • JSF/JSP: Avoid including raw user input in messages or components rendered on pages where EL is evaluated. If necessary, sanitize the input to remove or escape ${, #{, {, } characters. Use standard JSF components which often handle escaping.
  • SPeL: Do not construct SPeL expressions by concatenating user input. Use safe contexts (SimpleEvaluationContext) instead of StandardEvaluationContext if possible, as SimpleEvaluationContext restricts access to dangerous features like T() operator (type references) and bean references. If you must use user input, treat it strictly as data, not part of the expression logic (e.g., pass it as a variable to the context).

Secure Code Example

// JSF Backing Bean (Secure - Sanitized Output)
String userInput = request.getParameter("searchTerm");
// SECURE: Escape EL characters (simplistic example, needs robustness)
String safeInput = userInput.replace("$", "").replace("#", "").replace("{", "").replace("}", "");
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
    "Search failed for " + safeInput, null)); // Use sanitized input

// service/DynamicRuleService.java (Secure SPeL - Using Context Variable)
import org.springframework.expression.spel.support.StandardEvaluationContext;
// ... other imports ...
public boolean evaluateRuleSecure(String userInput, Object contextObject) {
    ExpressionParser parser = new SpelExpressionParser();
    // SECURE: Expression uses a variable '#inputVar'
    String expressionString = "#root.value > 10 AND (#inputVar != '')";
    try {
        Expression exp = parser.parseExpression(expressionString);
        // SECURE: User input is passed safely as a variable in the context.
        StandardEvaluationContext context = new StandardEvaluationContext(contextObject);
        context.setVariable("inputVar", userInput);
        return exp.getValue(context, Boolean.class);
    } catch (Exception e) {
         System.err.println("SPeL Error: " + e.getMessage());
         return false;
    }
}

Testing Strategy

Identify all points where user input might be reflected in JSF/JSP pages or used in SpelExpressionParser.parseExpression(). Submit payloads designed to trigger EL evaluation:
  • Simple math: ${7*7}, #{7*7}
  • Java method calls (JSF/SPeL): ${customer.getName()}, #{T(java.lang.System).getenv()}
  • OS commands (SPeL): #{T(java.lang.Runtime).getRuntime().exec('id')} Check if the expressions are evaluated or if errors related to parsing/execution occur.