# SpEL Injection in Spring: A Practical AppSec Guide with Examples

**SpEL Injection** is an important vulnerability for **Java, Spring Boot, and Application Security** engineers.

SpEL stands for **Spring Expression Language**. It allows Spring applications to evaluate expressions dynamically.

SpEL itself is **not a vulnerability**.

The vulnerability occurs when an application takes **attacker-controlled input and evaluates it as a SpEL expression**.

For an AppSec engineer, the most useful mental model is:

```text
Source → Data Flow → SpEL Sink → Evaluation Context → Impact
```

* * *

# 1\. What Is SpEL?

**SpEL (Spring Expression Language)** is an expression language provided by the Spring Framework.

It can work with:

*   Properties
    
*   Methods
    
*   Objects
    
*   Collections
    
*   Operators
    
*   Spring beans
    
*   Java types
    

A simple example:

```java
ExpressionParser parser =
        new SpelExpressionParser();

Expression exp =
        parser.parseExpression("2 + 3");

System.out.println(exp.getValue());
```

Result:

```text
5
```

SpEL can also access properties and methods:

```text
user.name
```

```text
user.getName()
```

This flexibility is useful for developers.

The security problem starts when **the expression itself comes from an untrusted user**.

* * *

# 2\. When Does SpEL Become a Vulnerability?

## SpEL Is NOT Automatically Vulnerable

Finding this:

```java
SpelExpressionParser
```

does **not** prove a vulnerability.

For example:

```java
ExpressionParser parser =
        new SpelExpressionParser();

Expression exp =
        parser.parseExpression("2 + 2");

return exp.getValue();
```

The expression is hardcoded.

```text
Developer
    ↓
"2 + 2"
    ↓
SpEL Parser
    ↓
Evaluation
```

There is no attacker-controlled input.

### Vulnerable Pattern

Now consider:

```java
@GetMapping("/evaluate")
public Object evaluate(HttpServletRequest request) {

    String expression =
            request.getParameter("expression");

    ExpressionParser parser =
            new SpelExpressionParser();

    Expression exp =
            parser.parseExpression(expression);

    return exp.getValue();
}
```

Now the flow is:

```text
Attacker
   ↓
HTTP Request
   ↓
request.getParameter("expression")
   ↓
Attacker-controlled input
   ↓
parseExpression(expression)
   ↓
SpEL
   ↓
getValue()
```

This is the classic **SpEL Injection** pattern.

> **The vulnerability is not the use of SpEL. The vulnerability is allowing untrusted input to become a SpEL expression.**

* * *

# 3\. Realistic Attack Surface

Suppose the application exposes:

```http
GET /evaluate?expression=10%20%2B%2020
```

The application executes:

```java
String expression =
        request.getParameter("expression");

Expression exp =
        parser.parseExpression(expression);

return exp.getValue();
```

The data flow is:

```text
HTTP Request
     ↓
expression=10+20
     ↓
request.getParameter()
     ↓
"10 + 20"
     ↓
parseExpression()
     ↓
SpEL
     ↓
getValue()
     ↓
30
```

The application intended to receive **data**.

Instead, it has allowed the user to provide **executable expression syntax**.

That is the fundamental security issue.

* * *

# 4\. Source → Sink Analysis

For source-code review, think in terms of **source and sink**.

## Common Sources

Look for attacker-controlled values coming from:

```java
request.getParameter("expression");

request.getHeader("X-Expression");

request.getInputStream();

jsonObject.get("expression");

cookie.getValue();
```

Other sources may include:

*   REST API JSON
    
*   HTTP headers
    
*   Cookies
    
*   Message queues
    
*   User-controlled configuration
    
*   Database values originally supplied by users
    

## SpEL Sinks

Search for:

```text
SpelExpressionParser
ExpressionParser
parseExpression()
Expression
getValue()
StandardEvaluationContext
SimpleEvaluationContext
```

The security-sensitive flow is:

```text
SOURCE
  ↓
Attacker-controlled input
  ↓
Application logic
  ↓
parseExpression()
  ↓
getValue()
  ↓
SpEL Evaluation
  ↓
Security Impact
```

This is much more useful than simply searching for `SpelExpressionParser`.

* * *

# 5\. Understanding SpEL Capabilities

Once you establish that attacker-controlled input reaches SpEL, determine **what the expression engine allows**.

A useful progression for an authorized security test is:

```text
Basic expression
      ↓
Property access
      ↓
Method invocation
      ↓
T() / Java type access
      ↓
new / object construction
      ↓
Sensitive Java APIs
      ↓
Potential RCE
```

Start with harmless expressions before testing more powerful capabilities.

* * *

# 6\. `T()` — Java Type Access

One of the most important SpEL constructs for AppSec engineers is:

```text
T(...)
```

For example:

```text
T(java.lang.String)
```

This references a Java type.

Another harmless example:

```text
T(java.lang.Math).PI
```

Conceptually:

```text
T(...)
 ↓
Java Type / Class
 ↓
Class Members
```

The security significance becomes much greater when `T()` can be combined with method invocation.

* * *

# 7\. `T()` → `Runtime` → `exec()` — Understanding RCE

A classic SpEL capability chain is:

```text
T(java.lang.Runtime)
        ↓
getRuntime()
        ↓
exec(...)
        ↓
Operating-system command
```

Conceptually:

```text
T(java.lang.Runtime).getRuntime().exec(...)
```

In an **authorized local lab**, a harmless demonstration could be:

```text
T(java.lang.Runtime).getRuntime().exec('touch /tmp/spel-pwned')
```

The important part is the chain:

```text
SpEL
 │
 ▼
T(java.lang.Runtime)
 │
 ▼
java.lang.Runtime
 │
 ▼
getRuntime()
 │
 ▼
Runtime instance
 │
 ▼
exec(...)
 │
 ▼
Operating-system command
```

If the application's SpEL configuration permits this capability, the command executes with the privileges of the application process.

This demonstrates why unrestricted SpEL injection can potentially become **Remote Code Execution (RCE)**.

### Important

Do not automatically report:

> **SpEL Injection = RCE**

Instead, establish whether the application actually permits:

```text
T()
 ↓
Runtime
 ↓
getRuntime()
 ↓
exec()
```

* * *

# 8\. `new` — Java Object Construction

Another important SpEL capability is:

```text
new
```

When permitted by the evaluation context, SpEL can construct Java objects.

For example:

```text
new java.io.File('/tmp/pwn')
```

This creates a `File` object.

Method invocation can then be used:

```text
new java.io.File('/tmp/pwn').createNewFile()
```

In a controlled lab, the security chain is:

```text
SpEL
 ↓
Java class
 ↓
Object construction
 ↓
Method invocation
 ↓
Filesystem operation
```

This demonstrates why **constructor access + method invocation** is important during a security assessment.

* * *

# 9\. Network APIs and SpEL

The same concept applies to Java APIs capable of network communication.

For example, in an authorized local environment:

```text
new java.net.URL('http://127.0.0.1:8080/').openStream() != null
```

Conceptually:

```text
new
 ↓
java.net.URL
 ↓
openStream()
 ↓
Network connection
```

If an attacker can reach network APIs through SpEL, the impact could extend into **SSRF or internal network interaction**.

Potential consequences include:

*   Access to internal services
    
*   Internal host probing
    
*   Cloud metadata access
    
*   Access to services unavailable externally
    

* * *

# 10\. SpEL Injection ≠ Automatically RCE

This distinction is critical.

Finding:

```text
request.getParameter()
        ↓
parseExpression()
        ↓
getValue()
```

establishes a strong SpEL injection condition.

But the actual impact depends on the application's capabilities.

Possible outcomes include:

```text
SpEL Injection
      │
      ├── Property access
      ├── Method invocation
      ├── Java type access
      ├── Object construction
      ├── File operations
      ├── Network operations
      └── Potential command execution
```

Therefore:

> **SpEL Injection is the vulnerability class. RCE is one possible impact.**

* * *

# 11\. EvaluationContext: The Critical Security Boundary

You may encounter:

```java
StandardEvaluationContext context =
        new StandardEvaluationContext();

expression.getValue(context);
```

The **EvaluationContext** is extremely important because it influences what the expression can access.

Think of it as the capability boundary around SpEL:

```text
                  SpEL
                   │
                   ▼
          Evaluation Context
                   │
       ┌───────────┼───────────┐
       ▼           ▼           ▼
     Types       Methods      Beans
       │           │           │
       └───────────┼───────────┘
                   ▼
              Application
```

During source review, determine:

*   Can `T()` be used?
    
*   Can `new` be used?
    
*   Which types are accessible?
    
*   Which constructors are accessible?
    
*   Which methods can be invoked?
    
*   Which Spring beans are exposed?
    
*   Which application objects are available?
    
*   Are custom resolvers/accessors configured?
    

These details determine the actual exploitability.

* * *

# 12\. `SimpleEvaluationContext`

Spring also provides:

```text
SimpleEvaluationContext
```

which is designed for more restricted expression evaluation.

The security principle is:

```text
Dynamic expression required
          ↓
Use least powerful context
          ↓
Restrict capabilities
          ↓
Expose only required functionality
```

However:

> **Do not treat** `SimpleEvaluationContext` **as a magic fix for SpEL injection.**

The strongest defense remains:

**Do not evaluate attacker-controlled expressions.**

* * *

# 13\. Safe Property DSL: The Better Design

Suppose the business requirement is:

> "Users should be able to create dependency-security policy conditions."

You usually do **not** need to expose the entire SpEL language.

Instead, create a small **Domain-Specific Language (DSL)** containing only the operations the business actually needs.

### Unsafe design

```text
User
  ↓
Full SpEL
  ↓
Java objects
  ↓
Methods / Types / Beans
  ↓
Potential abuse
```

### Safer design

```text
User
  ↓
Safe Property DSL
  ↓
Validate field + operator + value
  ↓
Allowlisted application logic
  ↓
Result
```

For example:

```json
{
  "field": "vulnerability.cvssScore",
  "operator": "gte",
  "value": 9
}
```

The application might allow only predefined fields:

```text
vulnerability.cvssScore
vulnerability.severity
vulnerability.cve
component.name
component.version
component.license
```

and operators:

```text
eq
neq
gt
gte
lt
lte
contains
startsWith
in
exists
```

It should also validate the value type:

```text
Number
String
Boolean
List
```

Anything outside the grammar is rejected.

### Safe DSL Flow

```text
User Input
    ↓
Safe DSL
    ↓
Schema Validation
    ↓
Allowed Field?
    ↓
Allowed Operator?
    ↓
Correct Value Type?
    ↓
Application Logic
```

There is no need to give the user access to:

```text
T()
new
method calls
beans
Java classes
```

### Why is this safer?

With full SpEL:

```text
expression
   ↓
SpEL parser
   ↓
T()
new
method calls
beans
Java classes
```

With a safe DSL:

```text
field ─────→ allowlist
operator ──→ allowlist
value ─────→ type validation
```

The user can express **business rules**, but cannot express arbitrary **Java/Spring operations**.

### AppSec Principle

> **Don't give users a general-purpose interpreter when they only need a small business-rule language.**

This principle applies beyond SpEL to:

*   SQL
    
*   Template engines
    
*   Command execution
    
*   Scripting engines
    
*   Query languages
    

* * *

# 14\. SpEL vs SSTI

**SpEL Injection** and **Server-Side Template Injection (SSTI)** are related but different vulnerabilities.

### SpEL Injection

```text
Attacker Input
      ↓
SpEL Parser
      ↓
Spring Expression Evaluation
```

### SSTI

```text
Attacker Input
      ↓
Server-Side Template Engine
      ↓
Template Evaluation
```

Common template technologies include:

*   Jinja2
    
*   FreeMarker
    
*   Velocity
    
*   Thymeleaf
    

The key difference is the interpreter being abused.

```text
SQL Injection
     ↓
SQL Interpreter

SSTI
     ↓
Template Interpreter

SpEL Injection
     ↓
Spring Expression Interpreter
```

SSTI can also have severe consequences, including sensitive data exposure, server-side actions, and potentially RCE depending on the template engine and configuration.

The common principle is:

> **Untrusted input must not unexpectedly become executable instructions.**

* * *

# 15\. SpEL vs SQL Injection

| SQL Injection | SpEL Injection |
| --- | --- |
| SQL interpreter | SpEL interpreter |
| Database context | Spring/Java context |
| SQL syntax | SpEL syntax |
| Database objects | Java/Spring objects |
| SQL functions | Methods/properties |
| Potential DB compromise | Potential application/JVM compromise |

Mental model:

```text
SQL Injection:

Attacker
   ↓
SQL
   ↓
Database
```

```text
SpEL Injection:

Attacker
   ↓
SpEL
   ↓
Spring Expression Engine
   ↓
Java/Spring Application
```

The common pattern is:

**Untrusted input → Powerful interpreter → Attacker-controlled execution**

* * *

# 16\. How to Find SpEL Injection During Source Review

Search for:

```text
SpelExpressionParser
ExpressionParser
parseExpression
Expression
getValue
StandardEvaluationContext
SimpleEvaluationContext
```

Also investigate Spring functionality that uses SpEL:

```text
@Value
@PreAuthorize
@PostAuthorize
@PreFilter
@PostFilter
@Cacheable
@CachePut
@CacheEvict
```

But don't report a vulnerability simply because one of these appears.

Trace the actual data flow.

For example:

```text
request.getParameter()
        ↓
Attacker-controlled input
        ↓
parseExpression()
        ↓
getValue()
```

is much more interesting than:

```java
@Value("#{someBean.someProperty}")
```

where the expression is entirely developer-controlled.

* * *

# 17\. SpEL Security Assessment Checklist

When you find a suspected SpEL injection, ask:

### 1\. Can the attacker control the expression?

```text
YES → Continue
NO  → Probably not injection
```

### 2\. Does it reach a SpEL sink?

Look for:

```java
parseExpression(input)
```

### 3\. Is the expression evaluated?

Look for:

```java
getValue()
```

or equivalent evaluation.

### 4\. Which EvaluationContext is used?

```text
StandardEvaluationContext
SimpleEvaluationContext
Custom context
```

### 5\. What capabilities are exposed?

Check:

```text
T()
new
Method invocation
Beans
Properties
Resolvers
Application objects
```

### 6\. What authentication is required?

Consider:

```text
Unauthenticated
Authenticated user
Administrator
```

### 7\. What is the actual impact?

Establish whether the attacker can achieve:

```text
Information disclosure
       ↓
Unauthorized object access
       ↓
File operations
       ↓
Network interaction / SSRF
       ↓
Potential command execution
```

* * *

# 18\. Prevention and Remediation

## Preferred Approach

**Don't evaluate attacker-controlled SpEL.**

Avoid:

```java
String expression =
        request.getParameter("expression");

parser.parseExpression(expression);
```

Instead, expose explicit application functionality or a restricted DSL.

For example:

```json
{
  "field": "vulnerability.cvssScore",
  "operator": "gte",
  "value": 9
}
```

Then evaluate that condition using predefined application logic.

* * *

## If Dynamic Expressions Are Absolutely Required

Use defense in depth:

*   Use the **most restrictive evaluation context**
    
*   Restrict Java types
    
*   Restrict constructors
    
*   Restrict method invocation
    
*   Restrict Spring beans
    
*   Avoid exposing sensitive application objects
    
*   Validate expression structure
    
*   Apply authorization
    
*   Monitor and audit expression evaluation
    

Avoid weak blacklists:

```java
if (input.contains("Runtime")) {
    reject();
}
```

or:

```java
if (input.contains("exec")) {
    reject();
}
```

Blacklists are fragile.

The stronger security boundary is:

> **Don't give untrusted users a general-purpose expression interpreter.**

* * *

# 19\. Interview Questions

### Q1. What is SpEL Injection?

**Answer:**

SpEL injection occurs when attacker-controlled input is interpreted as a **Spring Expression Language expression**. Depending on the evaluation context and accessible capabilities, an attacker may access application objects, invoke methods, perform file/network operations, or potentially achieve RCE.

### Q2. Is `SpelExpressionParser` itself vulnerable?

**Answer:**

**No.** SpEL is a legitimate Spring feature. The vulnerability occurs when **untrusted input reaches the expression parser/evaluator**.

### Q3. What is `T()`?

**Answer:**

`T()` allows SpEL to reference a **Java type/class**. It becomes particularly security-sensitive when an attacker can use it to reach powerful Java APIs.

### Q4. How can `T()` become an RCE primitive?

**Answer:**

Conceptually:

```text
T(java.lang.Runtime)
        ↓
getRuntime()
        ↓
exec(...)
        ↓
OS command
```

In an authorized local lab, a harmless demonstration is:

```text
T(java.lang.Runtime).getRuntime().exec('touch /tmp/spel-pwned')
```

This demonstrates the potential path from **SpEL → Java Runtime → OS functionality**, assuming the evaluation context permits it.

### Q5. What does `new` mean in SpEL?

**Answer:**

`new` can construct Java objects when permitted by the evaluation context. Combined with method invocation, this can expose capabilities such as filesystem or network operations.

### Q6. Does SpEL injection always mean RCE?

**Answer:**

**No.** RCE depends on the evaluation context, accessible types, constructors, methods, application configuration, authentication, and runtime restrictions.

### Q7. What is a safe property DSL?

**Answer:**

A safe property DSL is a **small, purpose-built language** exposing only the fields, operators, and value types required by the business. The application validates each component against an allowlist and evaluates the condition using predefined application logic instead of exposing a general-purpose expression engine.

### Q8. How do you prevent SpEL injection?

**Answer:**

The best defense is to **never evaluate attacker-controlled SpEL**. Prefer a narrowly defined property DSL when business rules are required. If dynamic expressions are unavoidable, use the most restrictive evaluation context possible and restrict types, constructors, methods, beans, and other accessible capabilities.

* * *

# 20\. Final AppSec Cheat Sheet

```text
                 SpEL INJECTION
                       │
                       ▼
          Attacker controls input?
                       │
                       ▼
              request.getParameter()
                       │
                       ▼
                parseExpression()
                       │
                       ▼
                  getValue()
                       │
                       ▼
              Evaluation Context
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
         T()          new       Method calls
          │            │            │
          └────────────┼────────────┘
                       ▼
                Java / Spring
                  capabilities
                       │
          ┌────────────┼─────────────┐
          ▼            ▼             ▼
        Data          File         Network
        Access      Operations     Access
                       │
                       ▼
                  Runtime APIs
                       │
                       ▼
                 Potential RCE
```

## The 7 Things to Remember

**1\. SpEL is not inherently vulnerable.**

**2\.** `request.getParameter()` **→** `parseExpression()` **→** `getValue()` **is the classic red flag.**

**3\.** `T()` **can reference Java types.**

**4\.** `T(java.lang.Runtime).getRuntime().exec(...)` **demonstrates the Java Runtime execution chain in a controlled lab.**

**5\.** `new` **can construct Java objects when permitted.**

**6\. The EvaluationContext determines what the expression can actually access and do.**

**7\. Prefer a safe, allowlisted property DSL when users only need to express business rules.**

### One-line interview answer

> **SpEL Injection occurs when attacker-controlled input reaches Spring's expression evaluator, allowing the attacker to influence expression execution and potentially access Java/Spring functionality, perform sensitive operations, or achieve RCE depending on the evaluation context and exposed capabilities.**

* * *

## The AppSec Mental Model

Don't memorize payloads first. Learn to trace:

```text
HTTP Request
     ↓
request.getParameter()
     ↓
Attacker-Controlled Input
     ↓
parseExpression()
     ↓
SpEL Evaluation
     ↓
EvaluationContext
     ↓
T() / new / Methods / Beans
     ↓
Java APIs
     ↓
Actual Security Impact
```

And when designing the application, reverse the model:

```text
User
  ↓
Safe Property DSL
  ↓
Schema Validation
  ↓
Allowlisted Fields
  ↓
Allowlisted Operators
  ↓
Type Validation
  ↓
Application Logic
  ↓
Result
```

That is the core **AppSec strategy**: don't expose a general-purpose interpreter when the business only needs a small, controlled language.
