Join our Newsletter — 33% off our NHI Course

What causes expression language injection?

Expression language injection happens when untrusted input is inserted into an expression parser and then executed as logic instead of treated as data. Attackers can read data, alter flow, or trigger privileged actions. Defenders should avoid building expressions from user input, use strict allow-lists, and escape or bind values before evaluation.

How expression language injection starts

expression language injection starts when an application treats user-controlled input as part of an expression instead of as plain data. The parser then evaluates that input with the application’s own privileges, so the attacker is no longer limited to supplying a string. The issue is usually a design flaw in input handling, not a flaw in the language itself.

This is why expression contexts are especially sensitive in templating, rules engines, workflow systems, and server-side frameworks that allow dynamic evaluation. If a developer concatenates request parameters into an expression, the parser may resolve variables, call methods, or trigger conditional branches that were never intended for user control.

A useful way to think about it is that the attack succeeds at the boundary between data and code. Once that boundary is crossed, even a small payload can change execution flow, expose internal values, or influence downstream actions. The safest design is to keep expressions static and bind only trusted values into them.

Where the security impact comes from

The impact depends on what the expression engine is allowed to do. In some systems, the result is limited to data disclosure or logic manipulation. In others, the expression can reach file operations, network calls, object methods, or authorization-sensitive decisions, which turns a parser bug into a high-impact application compromise.

That is why expression language injection often shows up alongside broader web application attack patterns. NHIMG’s Web Application Attacks Guide is a useful reference point for understanding how injection classes share the same core failure mode, untrusted input being interpreted as executable structure.

Two factors usually make the problem worse: broad expression capabilities and weak input boundaries. If the language can access application objects, the payload may move from simple condition changes to data access, method invocation, or privilege-sensitive actions. If the application also reflects evaluation errors, attackers gain feedback that helps refine the payload.

How to prevent expression language injection

Prevention is mostly about design discipline. Keep expressions out of user input paths, and when dynamic evaluation is unavoidable, make the expression template immutable while binding only vetted values into named placeholders. Allow-lists work better than deny-lists because expression grammars often have many alternate syntaxes and encoding tricks.

For appsec teams, the important distinction is between sanitising text and constraining evaluation. Escaping characters may help in some interpreters, but it is not a universal fix when the parser still receives attacker-controlled structure. Review every place where a request, header, cookie, or database value is passed into a template, rule engine, or formula field.

Testing should focus on expression boundaries, not just obvious injection points. Verify whether the application evaluates user-supplied values, whether error messages expose parser behaviour, and whether the expression engine can reach sensitive objects or actions. The strongest controls are removing dynamic evaluation where possible and reducing the parser’s privileges where it is not.

Risk and Threat Considerations

Expression language injection becomes more serious when the parser can influence business logic, access decisions, or sensitive object methods. Even if the payload does not lead to code execution, an attacker may still alter control flow, bypass checks, or read data that should never be exposed through a normal request path.

Failure mechanism: Untrusted input is accepted into an expression context, parsed as executable logic, and evaluated with application authority. Attackers exploit that confusion to inject operators, method calls, or conditional logic that changes what the application does.

Impact: The result can range from data exposure and workflow manipulation to unauthorized actions in security-critical code paths. In the worst cases, the injected expression becomes a pivot into broader application compromise because the engine runs inside a trusted process boundary.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

OWASP API Security Top 10 and MITRE ATT&CK address the attack and risk surface, while OWASP ASVS sets the governance and control requirements practitioners need to meet.

Framework Control / Reference Relevance
OWASP ASVS V1 — Encoding and Sanitization Expression injection is driven by unsafe handling of untrusted input before evaluation.
V15 — Secure Coding and Architecture The core fix is to avoid dynamic evaluation of user-controlled structure in application logic.
V16 — Security Logging and Error Handling Parser errors and evaluation traces can reveal exploitable expression behaviour.
Recommendation — Validate and encode user input before it can influence expression parsing. Redesign code so expressions stay static and user data is bound, not concatenated. Log expression failures safely and avoid exposing parser details to users.
OWASP API Security Top 10 API6 — Unrestricted Access to Sensitive Business Flows Injected expressions can alter business logic and sensitive request flows.
Recommendation — Protect sensitive flows from user-controlled expression logic.
MITRE ATT&CK T1059 — Command and Scripting Interpreter Expression engines can become interpreter surfaces when they execute attacker-controlled logic.
Recommendation — Hunt for interpreter-style execution paths that accept attacker-controlled expressions.

Practitioner Guidance

What to verify: Check every code path where user input can reach a template, formula field, rule engine, or expression parser. The key question is not whether the payload is “clean”, but whether the application ever evaluates attacker-controlled structure at all.

Common mistake: Teams often escape characters and assume the problem is solved, but many expression engines support alternate syntax, nested evaluation, or object navigation that bypasses simple filtering. If the language can resolve objects or invoke methods, the safer fix is to remove dynamic evaluation from the user path.

Practitioner takeaway: Treat expression parsing as code execution boundary, not just input validation. If untrusted data can shape the expression itself, the application should be redesigned so only trusted templates are evaluated and only bounded values are substituted.