CWE-94: Improper Control of Generation of Code (Code Injection)
Overview
Code injection occurs when an application dynamically generates and executes code using untrusted input, allowing attackers to inject and execute arbitrary code within the application's runtime environment. Unlike command injection (which executes OS commands), code injection executes in the application's programming language context with full access to application internals, variables, functions, database connections, file system, and secrets.
Common vulnerable patterns include eval(), exec(), Function(), and dynamic template rendering in unsafe mode across languages like Python, JavaScript, PHP, Ruby, Java, and C#.
OWASP Classification
A05:2025 - Injection
Risk
Code injection enables complete application compromise:
- Remote code execution: Execute arbitrary code in application context
- Data breach: Access databases, files, memory, and application secrets
- Authentication bypass: Manipulate session state, impersonate users
- Business logic tampering: Modify pricing, inventory, access controls
- Server takeover: Execute system commands, install backdoors
- Lateral movement: Pivot to internal systems and services
- Denial of service: Crash application, consume resources
- Supply chain attacks: Inject malicious code affecting all users
Single vulnerability can expose entire application and infrastructure.
Relationship to Other CWEs
- CWE-94 (This page): General code injection via dynamic code generation.
- CWE-95 (Eval Injection): Narrower subset focused specifically on unsafe use of eval().
Common Vulnerable Patterns
Dangerous patterns across languages:
- Python:
eval(user_input),exec(user_code),compile(),__import__() - JavaScript:
eval(userCode),new Function(userCode)(),setTimeout(userCode, 1000) - PHP:
eval($_GET['code']),assert(),create_function() - Ruby:
eval(params[:code]),instance_eval(),class_eval() - Java:
ScriptEngine.eval(userInput), BeanShell, Groovy evaluation - C#:
CSharpCodeProvider.CompileAssemblyFromSource(), Roslyn dynamic compilation
Code injection provides access to:
- Application variables and functions
- Database connections and queries
- File system and network access
- Cryptographic keys and secrets
- User sessions and authentication state
Remediation Steps
Core principle: Never execute dynamically generated code derived from untrusted input; remove eval/dynamic compilation or strictly sandbox with allowlists.
Trace the Data Path and Identify Code Execution Points
Analyze how untrusted data reaches dynamic code execution:
- Trace data flow from source to sink: Identify the path from where untrusted data originates to where it's executed as code
- Source: Where untrusted data enters (HTTP parameters, form inputs, file uploads, API requests, database fields populated by users, WebSocket messages)
- Sink: Code execution functions (
eval(),exec(),Function(),compile(),ScriptEngine, template rendering in unsafe mode) - Review your scan results: Check the specific file paths, line numbers, and variable names involved in the data flow
- Dangerous patterns: Search codebase for
eval(),exec(), dynamic template rendering, reflection APIs, deserialization of untrusted data - Look for transformations: Identify any sanitization attempts between source and sink that may be insufficient (string replacement, encoding, filters)
Eliminate Dynamic Code Execution (Primary Defense)
The safest approach is to remove dynamic code execution entirely:
DANGEROUS patterns to eliminate:
Python:
JavaScript:
PHP:
Java:
ScriptEngineManager.getEngineByName("JavaScript").eval(userInput)
BeanShell.eval(userCode)
Groovy.evaluate(userScript)
C#:
The only safe amount of eval() with user input is ZERO.
Replace with Safe Alternatives
Replace code execution with declarative, safe approaches:
Configuration-driven logic:
- JSON/YAML configuration files
- Rule engines (Drools, Easy Rules)
- Expression languages with restricted capabilities
- Template engines with auto-escaping (Jinja2, Handlebars)
Safe dynamic dispatch:
- Switch/case statements with pre-defined options
- Strategy pattern (pre-defined implementations)
- Command pattern with registered handlers
- Plugin systems with sandboxing
Data transformation:
- JSON path/jq for data queries
- XPath/XQuery for XML (with restrictions)
- SQL with parameters for database queries
- pandas/NumPy for data manipulation
Business rules:
- Decision tables
- Workflow engines
- State machines
- Declarative policy files
Sandbox and Restrict (Only If Absolutely Required)
If code execution cannot be avoided, use extreme isolation:
Sandboxing approaches:
Python:
- Use RestrictedPython (limited Python subset)
- ast.parse() + allowlist AST node types
- Run in Docker container with restricted capabilities
JavaScript:
- vm2 module (isolated V8 context)
- Web Workers with restricted APIs
- iframe with sandbox attribute
Java:
- OSGi bundles with limited imports
- GraalVM with restricted contexts
- Separate JVM process with limited classpath
General sandboxing requirements:
- Separate process with no network access
- Extremely short timeout (prevent DoS)
- Memory limits
- No access to filesystem, network, or system calls
- Allowlist of permitted functions/modules
Add Input Validation (Defense-in-Depth Only)
If sandboxing is used, add validation as additional protection:
- Maximum length limits (prevent DoS)
- Character allowlist (alphanumeric + specific chars only)
- Denylist dangerous keywords (eval, exec, import, system)
- AST parsing to detect dangerous constructs
- Regular expression matching for code patterns
WARNING: Validation alone is INSUFFICIENT. Bypasses are common and new attack vectors are discovered regularly. Use only as defense-in-depth with sandboxing.
Test Thoroughly
Verify your fixes with malicious inputs:
- Test code injection:
__import__('os').system('whoami'),eval('malicious') - Test object access:
this.constructor.constructor('return process')().env - Test import injection:
require('child_process').exec('ls') - Test encoding bypasses:
\x5f\x5fimport\x5f\x5f, Base64-encoded payloads - Ensure legitimate functionality still works correctly
- Re-scan with the security scanner to confirm the issue is resolved
- Document the changes for future reference
1. Basic Code Injection
# Test executing arbitrary code
input: "__import__('os').system('whoami')"
input: "open('/etc/passwd').read()"
input: "globals()['__builtins__']['eval']('malicious')"
input: "exec('import socket; ...')"
# Expected: Not executed, or sandboxed and blocked
Object/Variable Access
// Test accessing application internals
input: "this.constructor.constructor('return process')().env"
input: "require('child_process').exec('whoami')"
input: "Object.keys(global)"
# Expected: Access denied or variables undefined in sandbox
Import/Require Injection
# Test loading dangerous modules
input: "__import__('subprocess').call(['ls', '-la'])"
input: "import('fs').then(fs => fs.readFileSync('/etc/passwd'))"
input: "System.loadLibrary('malicious')"
# Expected: Import blocked or module not available
Encoding Bypass
Test with encoded payloads
input: "\x5f\x5fimport\x5f\x5f" (__import__)
input: "eval(atob('ZXZhbCgnYWxlcnQoMSknKQ=='))" (Base64)
input: "Function('return this')()" (access global)
Expected: Encoding doesn't bypass restrictions
Verify Removal
# Search codebase for dangerous functions
grep -r "eval(" --include="*.py" --include="*.js" --include="*.php"
grep -r "exec(" --include="*.py"
grep -r "new Function" --include="*.js"
grep -r "ScriptEngine" --include="*.java"
# Expected: No usage with user input, or all properly sandboxed
Language-Specific Guidance
For detailed implementation examples in specific programming languages:
- Python: See python/INDEX.md - ast.literal_eval, restricted AST parsing, Jinja2 sandboxing, pickle alternatives
- JavaScript/Node.js: See javascript/INDEX.md - math.js, vm2 sandboxing, avoiding eval/Function, CSP headers