đŸ›Ąïž Application Security CheatSheet

Expression Language (EL) Injection

Many apps let admins, power-users, or internal tools write expressions like “price * tax” or “if user is VIP then 
”. An Expression Language (EL) is a mini-language used to evaluate those expressions against data.

What I see in reviews: these issues usually slip through because everything looks “fine” in happy-path testing — until one weird request hits production.

EL Injection happens when the application evaluates an expression that contains untrusted input. The risk is not “a weird string” — it’s that the system starts treating attacker-controlled text as code-like logic.

Key idea: If user input crosses the boundary from data to expression, the attacker can influence execution and access to variables/functions.

Why EL injection exists (root cause)

Important: EL injection is a category of server-side injection similar to template injection or unsafe eval. The exact impact depends on what the expression engine can access.

Mental model: “expression engine = interpreter with a context”

Think of EL evaluation as running an interpreter with two things:

EL injection happens when Someone can control the expression string or meaningfully alter it, and the context is powerful enough to cause harm.

experienced interview line: “I focus on (1) who controls the expression, (2) what’s in the evaluation context, and (3) whether outputs influence security decisions.”

Vulnerable vs secure patterns (Node.js examples)

Vulnerable pattern: building an expression string

// Node/Express (example)
import { Parser } from "expr-eval"; // any expression engine / rules library

const parser = new Parser();

app.post("/pricing/preview", (req, res) => {
  // ❌ Risk: mixing untrusted input into the expression string
  // Example business intent: "basePrice * (1 - discount)"
  const base = Number(req.body.basePrice);
  const discount = String(req.body.discount); // user-controlled
  const expr = `basePrice * (1 - ${discount})`; // ❌ untrusted in expression string

  const value = parser.evaluate(expr, { basePrice: base });
  res.json({ value });
});

Secure pattern: keep user input as data, not expression

import { Parser } from "expr-eval";

const parser = new Parser();

// ✅ Expression is server-owned / allow-listed
const PRICING_FORMULAS = {
  "standard": "basePrice * (1 - discountRate)",
  "vip": "basePrice * (1 - discountRate) - vipBonus"
};

function safeNumber(x, fallback = 0) {
  const n = Number(x);
  return Number.isFinite(n) ? n : fallback;
}

app.post("/pricing/preview", (req, res) => {
  const formulaKey = String(req.body.formula || "standard");
  const expr = PRICING_FORMULAS[formulaKey] || PRICING_FORMULAS.standard;

  // User inputs are values in a constrained schema
  const ctx = {
    basePrice: safeNumber(req.body.basePrice, 0),
    discountRate: Math.min(Math.max(safeNumber(req.body.discountRate, 0), 0), 1),
    vipBonus: safeNumber(req.body.vipBonus, 0)
  };

  const value = parser.evaluate(expr, ctx);
  res.json({ value });
});
Why this works: the expression is server-controlled and the user only provides numeric values in a constrained range. You removed the “data → code” boundary crossing.

Where EL injection shows up (practical hotspots)

Risk depends on “context power”

Two EL evaluation setups can look similar but have very different risk based on what the expression can access.

Practical framing: EL injection is often about unexpected capability: the engine can do more than the business feature intended.

Exploitation progression (attacker mindset)

This describes attacker thinking at a high level (no copy/paste instructions). Attackers usually try to answer three questions:

Phase 1: Find an evaluation sink

Phase 2: Map the context

Phase 3: Look for impact levers

Interview takeaway: “I don’t start from payloads; I start from sinks, context, and impact levers.”

Tricky edge cases & conceptual bypass patterns

Safe validation workflow (defensive verification)

Goal: confirm whether untrusted input is evaluated as an expression and whether it can affect security outcomes — without providing exploit recipes.
  1. Inventory: endpoints/features that accept formulas/conditions/templates.
  2. Trace data flow: identify if user input is concatenated into an expression string or stored and later evaluated.
  3. Map context: list exposed variables/functions; check whether request/config/DB helpers are in scope.
  4. Assess impact: does evaluation influence authz, routing, data filtering, or write actions?
  5. Check guardrails: input validation, allow-lists, sandboxing, timeouts, and rate limits.
  6. Prove safely: demonstrate “expression evaluation present” using benign expressions and observe predictable output changes.

Defensive patterns & mitigations

1) Prefer server-owned expressions (allow-list)

2) Minimize the evaluation context

3) Constrain language features

4) Add safety controls

Rule of thumb: If expressions are user-controlled, treat the engine like code execution risk and design strong policy + sandbox controls.

Confidence levels (how sure are you?)

Checklist (quick review)

Remediation playbook

  1. Contain: disable or restrict user-controlled expressions; force safe presets.
  2. Fix root cause: remove concatenation of untrusted input into expression strings; pass user values via typed variables.
  3. Reduce context: strip powerful objects from scope; expose only minimal primitives.
  4. Constrain engine: limit operators/features; enforce length and complexity limits; add timeouts.
  5. Review usage: search codebase for expression evaluation sinks and templating/rule engines.
  6. Test: add regression tests ensuring user input cannot alter expression structure; validate DoS protections.
  7. Monitor: log expression evaluation errors/timeouts and alert on unusual spikes.

Interview-ready answers (60-second + 2-minute)

60-second answer

EL injection is when an app evaluates an expression that includes untrusted input, turning attacker-controlled text into code-like logic. I assess it by checking who controls the expression string, what the evaluation context exposes, and whether results affect security decisions. Fixes are policy-driven: keep expressions server-owned (allow-lists), pass user input as values not expression fragments, minimize context, and add limits like timeouts and complexity controls.

2-minute answer

I treat expression evaluation as an interpreter plus a context. If user input can shape the expression, or the context exposes powerful objects, you can get integrity, confidentiality, or availability impact. I inventory formula/template/rule features, trace data flow to see if input is concatenated into an expression string, then map what variables/functions are in scope. I’m especially cautious if the expression result controls authorization, routing, or data filtering. Remediation is to use server-owned templates/formulas, restrict expression features, pass user inputs through typed variables, minimize context to primitives, and add guardrails like timeouts, rate limiting, auditing, and regression tests.

Interview Questions & Answers (Easy → Hard)

How to answer: Start simple (what it is), then explain the boundary (data vs expression), then talk about context + impact + mitigations.

Easy

  1. What is EL injection?
    A: Layman: It’s when user input gets treated like a formula the server executes. Deep: The expression engine interprets strings with a context. If untrusted data can control the expression or meaningfully change it, you have a code-like injection risk.
  2. How is EL injection different from SQL injection?
    A: Layman: SQLi targets database queries; EL injection targets expression evaluation. Deep: Both are “data becomes code” issues, but EL injection depends on the expression engine’s features and context, not the database parser.
  3. What are typical business features that introduce EL evaluation?
    A: Layman: Custom formulas and rules. Deep: Discounts, scoring rules, feature flags, workflow conditions, report calculated fields, and template-like systems.
  4. What are the three things you check first?
    A: Layman: Who controls the expression, what it can access, and what it affects. Deep: (1) expression source (server-owned vs user), (2) evaluation context (objects/functions), (3) impact levers (authz, data access, writes, DoS).
  5. What’s the simplest safe fix?
    A: Layman: Don’t let users write expressions. Deep: Use allow-listed formulas/templates controlled by the server and pass user input as typed values, not expression fragments.
  6. Why can “escaping” be unreliable here?
    A: Layman: Because expressions aren’t plain text. Deep: Different engines have different grammars; escaping rules vary; normalization/decoding can reintroduce meaning. Policy (allow-lists + minimal context) is stronger than string escaping.

Medium

  1. Scenario: A “pricing formula” field is editable by store managers. What’s your risk analysis?
    A: Layman: If they can change the formula, they can change business logic. Deep: Determine if it’s server-side evaluation, what context is exposed, and whether it can access sensitive variables. Add role restrictions, allow-list formulas, and constrain expression features with auditing.
  2. Scenario: Users can create workflow conditions (“if X then Y”). What do you look for?
    A: Layman: Whether those conditions are executed as code. Deep: Check if conditions are evaluated by an expression engine, ensure the context is minimal, and verify the actions are still authorized (don’t let expression results bypass permission checks).
  3. Scenario: A filter in a report builder changes which records a user can see. Why is this high risk?
    A: Layman: Because it affects data access. Deep: If expression results influence server-side filtering, a weak boundary or shared context can cause data exposure. Ensure filters are translated to safe queries with strong tenant scoping and allow-lists.
  4. Follow-up: How do you prove EL evaluation exists safely?
    A: Layman: Use harmless expressions and see predictable output changes. Deep: Validate evaluation without attempting dangerous operations; focus on evidence of interpretation, context variables, and impact mapping.
  5. Follow-up: What guardrails do you add for availability?
    A: Layman: Limits so expressions can’t overload the server. Deep: Length/complexity limits, timeouts, rate limits, caching of compiled expressions, and monitoring for spikes in evaluation time/errors.
  6. Follow-up: What is “overpowered context” and why does it matter?
    A: Layman: It’s giving the expression too many tools. Deep: If the context includes request/session/config/db helpers, the expression can potentially read/alter more than intended. Reduce to primitives and safe helper functions only.
  7. Scenario: Expression is stored in DB and evaluated by a nightly job. What changes in your threat model?
    A: Layman: The impact can be broader and delayed. Deep: Stored expressions can affect multiple users/tenants and run with higher privileges. Add strict write controls, approval workflows, auditing, and isolation of job permissions.

Hard

  1. Scenario: Multi-tenant app uses tenant-configured rules to route requests. What could go wrong?
    A: Layman: A tenant might break rules and affect others. Deep: If routing decisions come from evaluated expressions, weak scoping often leads to cross-tenant impact. Ensure tenant isolation in context, restrict rule capabilities, and validate rule outputs against safe allow-lists.
  2. How do you distinguish EL injection from safe parameterization?
    A: Layman: Safe systems pass values, not code. Deep: If user inputs are bound as typed variables in a server-owned expression/template, it’s safer. If user inputs alter expression structure or introduce operators/functions, it’s risky.
  3. Follow-up: If business requires “custom formulas”, what’s your secure design?
    A: Layman: Give them limited building blocks. Deep: Provide a constrained DSL with allow-listed operators, no object access, no function calls unless safe, strict context primitives, compile-time validation, quotas, and approval/auditing for changes.
  4. Follow-up: What do you log/monitor in production?
    A: Layman: Strange or expensive formula activity. Deep: Expression creation/updates, evaluation errors/timeouts, latency percentiles for evaluation endpoints, and abnormal spikes per tenant/user. Correlate with feature usage and incident signals.
  5. Scenario: Expression result is used to decide whether to allow an action (“if condition then allow”). Why is this dangerous even if the engine is sandboxed?
    A: Layman: Because it can bypass permissions. Deep: Even a safe sandbox can produce a “true/false” that changes authorization. Authorization must remain enforced by server-side policy checks; expressions may inform UI or scoring, not final access control.
  6. Follow-up: How do you scale prevention across a large Node.js codebase?
    A: Layman: Standardize and scan for unsafe patterns. Deep: Provide a shared safe evaluation wrapper, ban ad-hoc eval-like usage, add lint rules/code scanning for string-to-execution APIs, and centralize rule configuration with reviews and tests.
  7. Scenario: A “template preview” feature evaluates expressions with user profile data. What privacy concerns come up?
    A: Layman: It might expose data it shouldn’t. Deep: Ensure the context only includes permitted fields, enforce field-level access controls, and prevent users from referencing arbitrary objects. Validate that preview runs under the requester’s permissions and tenant scope.
Safety note: for understanding +