Eval Usage Analyzer
| Analyzer ID | Category | Severity | Time To Fix |
|---|---|---|---|
eval-usage | 🛡️ Security | Critical | 20 minutes |
What This Checks
Detects eval() and related dynamic code execution patterns. Severity escalates to Critical when tainted user-input variables (tracked from $_GET, request(), $request->, etc.) flow into any dangerous call, including pre-assigned variables like $fn = request('x'); ob_start($fn).
Detected Patterns:
Code Execution Functions (4)
eval()- executes arbitrary PHP codeassert()with a string literal - treated as code in PHP < 8.3create_function()- deprecated (PHP 7.2), removed (PHP 8.0); usesevalinternallypreg_replace()with/emodifier - executes replacement as PHP code (removed PHP 7.0)
Dynamic Invocation with User Input (5)
Flagged when the callable or class name is tainted by user input:
call_user_func()/call_user_func_array()with a user-controlled function name$func()- variable function calls$obj->$method()- dynamic method dispatchClass::$method()- dynamic static dispatchnew $class()- dynamic class instantiation
Unsafe Deserialization (1)
unserialize()with user input - Critical; PHP object injection / RCEunserialize()without a safeallowed_classesrestriction - Medium; flagged even without user input. Resolved by['allowed_classes' => false](no objects needed) or['allowed_classes' => [MyClass::class, ...]](explicit known-class list).trueor an empty[]still warns.
User-Controlled Callbacks and Templates (4)
ob_start()with a user-supplied callableregister_shutdown_function()with a user-supplied callableregister_tick_function()with a user-supplied callableBlade::compileString()with user input -@phpdirectives allow arbitrary PHP execution
Why It Matters
Dynamic code execution is one of the most dangerous vulnerability classes, allowing attackers to run arbitrary PHP code on your server:
- Remote Code Execution (RCE) - Complete server takeover by injecting malicious PHP code
- Data Exfiltration - Reading sensitive files, environment variables, database credentials
- Backdoor Installation - Persistent server access by writing malicious files
- Privilege Escalation - Executing system commands as the web server user
- Lateral Movement - Using the compromised server to attack internal services
- PHP Object Injection - Triggering
__wakeup,__destruct, or magic methods on existing classes viaunserialize()
A single dynamic code execution call with user input gives attackers the same access as your PHP process, making it one of the most critical security vulnerabilities possible.
How to Fix
Quick Fix (5 minutes)
Replace dynamic code execution with proper control structures or callbacks:
Before (❌):
public function calculate(Request $request)
{
$formula = $request->input('formula');
eval($formula); // VULNERABLE
}After (✅):
public function calculate(Request $request)
{
$validated = $request->validate([
'operand1' => 'required|numeric',
'operand2' => 'required|numeric',
'operator' => 'required|in:add,subtract,multiply,divide',
]);
$result = match ($validated['operator']) {
'add' => $validated['operand1'] + $validated['operand2'],
'subtract' => $validated['operand1'] - $validated['operand2'],
'multiply' => $validated['operand1'] * $validated['operand2'],
'divide' => $validated['operand2'] != 0
? $validated['operand1'] / $validated['operand2']
: throw new \InvalidArgumentException('Division by zero'),
};
return response()->json(['result' => $result]);
}Proper Fix (20 minutes)
Replace create_function() with closures:
Before (❌):
// VULNERABLE: create_function() uses dynamic evaluation internally
$sorter = create_function('$a, $b', 'return $a["name"] <=> $b["name"];');
usort($items, $sorter);After (✅):
// SAFE: Arrow function (PHP 7.4+)
usort($items, fn($a, $b) => $a['name'] <=> $b['name']);Replace preg_replace() with /e modifier:
Before (❌):
// VULNERABLE: /e modifier executes replacement as PHP code
$result = preg_replace('/\{(\w+)\}/e', '$data["$1"]', $template);After (✅):
// SAFE: preg_replace_callback() with closure
$result = preg_replace_callback('/\{(\w+)\}/', function ($matches) use ($data) {
return $data[$matches[1]] ?? '';
}, $template);Replace assert() with string argument:
Before (❌):
// VULNERABLE: assert() with string executes code
assert('$user->isValid()');After (✅):
// SAFE: assert() with boolean expression
assert($user->isValid());
// In production, disable assertions entirely:
// zend.assertions = 0Secure call_user_func() usage:
Before (❌):
public function execute(Request $request)
{
$callback = $request->input('action');
call_user_func($callback, $request->input('data')); // VULNERABLE
}After (✅):
public function execute(Request $request)
{
$validated = $request->validate([
'action' => 'required|in:process,validate,transform',
'data' => 'required|string',
]);
$callbacks = [
'process' => [$this, 'processData'],
'validate' => [$this, 'validateData'],
'transform' => [$this, 'transformData'],
];
call_user_func($callbacks[$validated['action']], $validated['data']);
}Fix unserialize() usage:
Before (❌):
// VULNERABLE: user-controlled data
$object = unserialize($request->input('payload'));
// UNSAFE: no class restriction
$cached = unserialize($data);After (✅):
// SAFE: reject user input entirely — use JSON instead
$data = json_decode($request->input('payload'), true);
// SAFE: restrict allowed classes when deserializing internal data
$cached = unserialize($data, ['allowed_classes' => false]);
// SAFE: allow only specific trusted classes
$cached = unserialize($data, ['allowed_classes' => [MyValueObject::class]]);Fix dynamic class instantiation:
Before (❌):
$class = $request->input('driver');
$instance = new $class(); // VULNERABLEAfter (✅):
$validated = $request->validate(['driver' => 'required|in:mysql,sqlite,pgsql']);
$drivers = [
'mysql' => MySqlDriver::class,
'sqlite' => SqliteDriver::class,
'pgsql' => PgsqlDriver::class,
];
$instance = new $drivers[$validated['driver']]();Fix callback registration with user input:
Before (❌):
// VULNERABLE: user controls the callback
ob_start($request->input('handler'));
register_shutdown_function($request->input('fn'));After (✅):
// SAFE: use a fixed, static callback
ob_start(function () {
// process output
});
// SAFE: register only known, internal functions
register_shutdown_function([$this, 'cleanup']);Fix Blade::compileString() with user input:
Before (❌):
// VULNERABLE: user-controlled Blade template compiles arbitrary @php blocks
$html = Blade::compileString(request('template'));After (✅):
// SAFE: pass user data as variables, never as the template itself
return view('templates.user-content', [
'content' => $request->input('content'),
]);References
- OWASP Code Injection
- CWE-94: Improper Control of Generation of Code
- CWE-95: Eval Injection
- CWE-502: Deserialization of Untrusted Data
- PHP eval() Documentation
- PHP assert() Documentation
- PHP preg_replace_callback Documentation
- PHP unserialize() Documentation
Related Analyzers
- Command Injection Analyzer - Detects shell command injection vulnerabilities
- RCE Analyzer - Detects remote code execution via variable functions and deserialization
- Object Injection Analyzer - Detects unsafe deserialization leading to code execution
- SQL Injection Analyzer - Detects SQL injection vulnerabilities
- XSS Vulnerabilities Analyzer - Detects cross-site scripting