# Laravel Dangerous Function Calls: How to Eliminate eval, shell_exec, and system from Your Codebase

> Functions like eval(), shell_exec(), system(), and proc_open() execute arbitrary code. If reachable from user input, they give attackers full server access.

**Severity:** critical | **Category:** Application Security

---

## The Issue

Dangerous sink functions — eval(), shell_exec(), system(), exec(), proc_open(), passthru(), and popen() — execute arbitrary system commands or PHP code. If any user input reaches these functions without strict sanitization, an attacker can execute any command on your server: reading files, installing backdoors, or pivoting to other systems. Even indirect paths through variable concatenation or string interpolation create exploitable attack vectors.

## Steps to Fix

### 1. Find all dangerous function calls in your codebase

Search for dangerous functions across your project:

grep -rn 'eval\|shell_exec\|system\|exec\|proc_open\|passthru\|popen\|pcntl_exec' app/ routes/ resources/

Also check for backtick operator usage:

grep -rn '`.*\$' app/

Document each occurrence and trace whether user input can reach it.

### 2. Replace with safe Laravel alternatives

Laravel provides safe alternatives for most use cases:

// Instead of shell_exec('git log')
use Symfony\Component\Process\Process;
$process = new Process(['git', 'log', '--oneline', '-10']);
$process->run();

// Instead of exec('convert image.png ...')
use Intervention\Image\Facades\Image;
Image::make($path)->resize(300, 200)->save();

// Instead of eval() for dynamic logic
use a strategy pattern, config-driven behavior, or Blade::render()

The Symfony Process component is preferred because it uses array arguments, preventing shell injection by design.

### 3. If you must use process execution, sanitize strictly

When shell commands are unavoidable, never pass user input directly:

// DANGEROUS
shell_exec('ffmpeg -i ' . $request->input('file'));

// SAFE — use escapeshellarg for each argument
$file = escapeshellarg($validated['file']);
shell_exec('ffmpeg -i ' . $file);

// SAFEST — use Symfony Process with array arguments
use Symfony\Component\Process\Process;
$process = new Process(['ffmpeg', '-i', $validated['file']]);
$process->setTimeout(60);
$process->run();

Array-based process execution prevents shell metacharacter injection entirely.

### 4. Disable dangerous functions in php.ini

On production servers, disable functions you never need:

; php.ini
disable_functions = eval,exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec

Note: Some packages legitimately need exec() or proc_open() (like Symfony Process). Only disable functions your application does not require. Test thoroughly after making changes.

## Verification

Run the Stackshield scanner:

php artisan stackshield:scan --check=SS003

Manually verify by searching for dangerous functions:

grep -rn 'eval(\|shell_exec(\|system(' app/ --include='*.php'

Any remaining occurrences should be documented with justification and wrapped in strict input validation.

## Prevention

Add a PHPStan or Psalm rule to flag dangerous function calls in CI. Use Symfony Process for all external command execution. Never concatenate user input into shell commands. Use StackShield to continuously scan for new dangerous sink introductions.

---

## Frequently Asked Questions

### Is escapeshellarg() enough to prevent command injection?

escapeshellarg() prevents most injection attacks by wrapping the argument in single quotes and escaping existing quotes. However, it has edge cases on Windows and doesn't protect against all attack vectors. The Symfony Process component with array arguments is the safest approach.

### What about eval() in Blade templates?

Blade compiles to PHP with eval-like behavior internally, but this is safe because template code is developer-controlled. The danger is using eval() directly in controllers or services with user-supplied strings. Never do eval($request->input('code')).

### Can attackers exploit shell_exec() if I only pass a filename?

Yes. A filename like "file.png; rm -rf /" contains a shell metacharacter (;) that terminates the first command and starts a new one. Always use escapeshellarg() or array-based Process execution, even for seemingly safe values like filenames.

### Is Artisan::call() considered a dangerous sink?

No. Artisan::call() invokes Laravel console commands within the PHP process and does not execute OS shell commands. It is safe to call dynamically as long as the command name itself is validated against an allowlist of known commands, preventing an attacker from triggering arbitrary artisan commands.

### Can I use proc_open() safely?

Yes, with array-based command arguments. Pass the command as an array: proc_open(["ffmpeg", "-i", $input], ...) instead of a shell string. Array-based invocation bypasses the shell entirely, preventing metacharacter injection regardless of what the input contains. The Symfony Process component wraps this pattern and is the recommended approach.

### What is the difference between command injection and code injection?

Command injection occurs through shell-executing functions (shell_exec, system) where attackers insert shell metacharacters to run OS commands. Code injection occurs through eval() where attackers inject PHP code that executes in the PHP process. Both result in arbitrary execution but through different mechanisms. eval() is almost never legitimate in application code; shell-executing functions can be safe when used with array-based Process execution.

