Safeguard
Application Security•

CWE-917 Expression Language Injection: From JSP to Log4Shell

When input is evaluated as an expression, attackers run code. CWE-917 covers EL injection in Java, the Log4Shell case and how to stop it.

Safeguard Security Team
4 min read

CWE-917, Improper Neutralization of Special Elements used in an Expression Language Statement, known as Expression Language Injection, applies when a product builds all or part of an expression language (EL) statement in a framework such as Java Server Pages, using externally influenced input, and does not neutralize special elements that could change the statement before it runs. MITRE lists Java as the platform.

If you run Java web applications or libraries that interpolate strings, this is relevant.

How it happens

Frameworks like JSP let developers put executable expressions inside otherwise static content. When developers do not realize those expressions execute, or do not turn them off, an attacker who can inject an expression can cause code execution or other unexpected behavior.

A simplified sketch of the pattern:

// Vulnerable: user input is placed inside an expression string
String expr = "#{user." + request.getParameter("field") + "}";
Object value = expressionFactory
    .createValueExpression(elContext, expr, Object.class)
    .getValue(elContext);

The attacker supplies field content that closes the property access and adds their own expression. The framework evaluates it with the application's privileges.

The headline example in the record is CVE-2021-44228 (details), the Log4Shell vulnerability in log4j. MITRE describes it as a product that does not neutralize dollar sign and brace style expressions, allowing remote code execution. A logging call that merely recorded attacker controlled text was enough, because the logging library interpreted special syntax inside the message.

${prefix:value}

That is the shape of the syntax in question. Any text that reaches the evaluator in this form is treated as an instruction. The lesson is that you do not need to write an expression to be exposed. A library you depend on may evaluate strings you pass it.

What an attacker gains

MITRE lists confidentiality, because application data can be read, and integrity, because unauthorized code or commands can be executed. Remote code execution means the attacker acts with the service's rights: reading secrets from memory and the environment, reaching internal systems, and changing data.

How to find it

MITRE notes automated static analysis (SAST) can find some instances by tracing data flow from sources of input to sinks, with high effectiveness. For EL injection the sink is the call that evaluates an expression string.

For the library angle, static analysis of your own code is not enough. You also need to know which dependencies you ship and which versions. A software composition scan lists them and matches them against known vulnerabilities. The free in-browser scan at gold.safeguard.sh/scan needs no login, keeps the file on your machine, and covers up to 100 dependencies. Reachability analysis then shows whether your code actually calls the vulnerable function, so the findings can be ordered by real exposure.

How to fix it

From MITRE:

  1. In design, avoid adding user controlled data to an expression interpreter when possible.
  2. If you must, do one or more of these: validate that the input will not evaluate as an expression, or encode it so it will not be evaluated as one.
  3. In system configuration and operation, use framework settings that disable processing of EL expressions. MITRE gives the example of setting the isELIgnored attribute of a JSP page to true.

Additional practice that follows from the Log4Shell case: keep libraries that interpolate strings up to date, and know where they sit in your dependency tree, including transitive ones.

Fixed sketch for the earlier code:

// Fixed: user input selects from a fixed list, never forms the expression
Set<String> allowed = Set.of("name", "email");
String field = request.getParameter("field");
if (!allowed.contains(field)) { throw new IllegalArgumentException(); }
Object value = user.get(field);

Try it yourself

The demo shows how a dependency inventory surfaces a vulnerable logging library and what to do next.

Click through it at your own pace, or open it full screen.

Quick check

What does isELIgnored do?

Setting it to true on a JSP page tells the framework not to process EL expressions on that page.

How can a logging library cause expression injection?

If it interprets special expression syntax inside logged text, attacker controlled input in a log message gets evaluated. That is the Log4Shell pattern in CVE-2021-44228.

Is input validation enough?

It is one of the options MITRE lists. Avoiding user data in the interpreter at all is listed first and is the stronger choice.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.