Skip to content

CWE-95: Eval Injection

Overview

Eval Injection occurs when untrusted input is passed to dynamic code execution functions (such as eval, exec, or similar), allowing attackers to execute arbitrary code within the application context. Untrusted input can originate from HTTP requests, external APIs, databases, files, message queues, or any source outside the application's control.

OWASP Classification

A05:2025 - Injection

Risk

Critical: Attackers can execute arbitrary commands, steal data, escalate privileges, or fully compromise the application and underlying system.

Relationship to Other CWEs

  • CWE-94 (Code Injection): Broader category covering all forms of dynamic code generation.
  • CWE-95 (This page): Eval-specific code injection.

Remediation Steps

Core principle: Never execute dynamically generated code derived from untrusted input; eliminate eval/exec-style evaluation and replace it with safe parsers or allowlisted interpreters.

Trace the Data Path

Analyze how untrusted data reaches code execution:

  • Source: Identify where untrusted data enters (user input, external files, databases, network requests, configuration)
  • Data Flow: Trace how data moves through the application
  • Sink: Locate code execution functions (eval(), exec(), Function(), compile(), ScriptEngine)
  • Missing Protection: Identify lack of validation or use of dangerous functions

Eliminate Dynamic Code Execution (Primary Defense)

Never use eval() or similar functions with untrusted data:

  • Remove completely: Refactor code to eliminate eval, exec, Function(), etc.
  • Use safe alternatives: Mapping, parsing, configuration files, expression evaluators
  • Replace with declarative approaches: JSON/YAML config, rule engines, template engines

Common dangerous functions to eliminate:

  • Python: eval(), exec(), compile(), __import__()
  • JavaScript: eval(), new Function(), setTimeout(string)
  • PHP: eval(), assert(), create_function()
  • Java: ScriptEngine.eval(), reflection with untrusted input

Why this works: If no dynamic code execution exists, code injection is impossible.

Use Safe Alternatives

Replace code execution with safer approaches:

For mathematical expressions:

  • Use safe expression evaluators (math.js, expr-eval)
  • Parse to AST and allowlist operators
  • Use operator mapping (map "+" to addition function)

For configuration/logic:

  • JSON/YAML configuration files
  • Rule engines (Drools, Easy Rules)
  • Strategy pattern with pre-defined implementations
  • Template engines with auto-escaping

For dynamic dispatch:

  • Switch/case statements with enumerated options
  • Command pattern with registered handlers
  • Plugin systems with sandboxing

If Execution Unavoidable: Sandbox and Restrict

Only if absolutely required (last resort):

  • Run in isolated process: Separate process with no network/filesystem access
  • Implement strict timeouts: Prevent DoS (milliseconds, not seconds)
  • Memory limits: Restrict resource consumption
  • Allowlist operations: Only permit specific functions/modules
  • Use sandboxing libraries: vm2 (Node.js), RestrictedPython, GraalVM

Critical: Sandboxing is defense-in-depth. Prefer elimination.

Add Input Validation (Defense in Depth Only)

If sandboxing is used, add validation:

  • Length limits: Prevent DoS
  • Character allowlist: Alphanumeric + specific allowed chars only
  • Denylist dangerous keywords: eval, exec, import, system, etc.
  • AST parsing: Detect dangerous constructs before execution

Warning: Validation alone is INSUFFICIENT. Bypasses are common. Use WITH sandboxing, never instead.

Test Thoroughly

Verify your fixes:

  • Test code injection: __import__('os').system('whoami'), eval('malicious')
  • Test object access: constructor.constructor('return process')()
  • Test import injection: require('child_process').exec('ls')
  • Test encoding bypasses: \x5f\x5fimport\x5f\x5f, Base64-encoded payloads
  • Ensure legitimate functionality works
  • Re-scan with security scanner

Common Vulnerable Patterns

  • Passing untrusted input to eval, exec, or similar functions
  • Using dynamic code execution for configuration or logic

Language-Specific Code Examples

For detailed, production-ready code examples in your programming language, see:

  • Python - Safe expression evaluation with AST, operator mapping, secure configuration, and alternatives to eval/exec/pickle
  • Java - Safe alternatives to ScriptEngine, SpEL, OGNL, reflection, and Groovy evaluation
  • JavaScript/Node.js - Safe math parsers, operation mapping, controlled module loading, and CSP implementation

Each language-specific guide includes:

  • Complete vulnerable and secure code patterns
  • Framework-specific implementations
  • Testing examples

Generic Secure Patterns

Use Explicit Operation Mapping

Replace dynamic code execution with predefined operations:

  • Define an allowlist of safe operations
  • Map untrusted input to pre-defined functions
  • Validate all inputs before execution
  • Never execute user-provided code strings

Safe Expression Evaluation

For mathematical or logical expressions:

  • Use AST (Abstract Syntax Tree) parsing
  • Implement a custom parser with operator allowlist
  • Use well-tested libraries designed for safe evaluation
  • Reject any unexpected operations or syntax

Controlled Plugin/Module Loading

For dynamic functionality:

  • Maintain an explicit allowlist of approved modules
  • Map untrusted input to allowlisted paths only
  • Validate plugin interfaces before execution
  • Never use untrusted input directly in import/require statements

Language-Specific Guidance

  • Java - Avoid ScriptEngine with untrusted input, use safe alternatives
  • JavaScript/Node.js - Avoid eval, use JSON.parse and safe-eval alternatives
  • Python - Avoid eval/exec, use ast.literal_eval for safe parsing

Dynamic Scan Guidance

For guidance on remediating this CWE when detected by dynamic (DAST) scanners:

Additional Resources