Die PHP-Funktion eval() führt einen String als PHP-Code aus. Das klingt erst einmal praktisch, ist aber eines der größten Sicherheitsrisiken in PHP. In fast allen Fällen gibt es bessere und sicherere Alternativen. Dieses Tutorial erklärt, wie eval() funktioniert, warum es gefährlich ist und welche Alternativen zur Verfügung stehen.
Wichtiger Hinweis: eval() sollte in neuem Code nicht verwendet werden. Es öffnet die Tür für Code-Injection-Angriffe und macht den Code schwer wartbar und testbar. Wer eval() in bestehendem Code findet, sollte es durch sichere Alternativen ersetzen.
Syntax und Funktionsweise
eval() ist streng genommen keine Funktion, sondern ein Sprachkonstrukt. Es erwartet einen String, der gültigen PHP-Code enthält, und führt diesen aus:
<?php
eval(string $code): mixed
?>
Der übergebene String muss gültigen PHP-Code enthalten. Das schließende PHP-Tag ?> am Ende des Strings ist nicht nötig, aber jede Anweisung muss mit einem Semikolon abgeschlossen werden.
Rückgabewert und return in eval()
Das Verhalten des Rückgabewerts überrascht viele Entwickler: eval() gibt standardmäßig null zurück. Nur wenn der ausgeführte Code eine return-Anweisung enthält, wird deren Wert zurückgegeben.
<?php
/* Ohne return: Ergebnis ist null */
$ergebnis = eval('$x = 10 + 20;');
var_dump($ergebnis); // NULL
echo $x; // 30 (Variable existiert!)
/* Mit return: Wert wird zurueckgegeben */
$ergebnis = eval('return 10 + 20;');
var_dump($ergebnis); // int(30)
?>
Wichtig zu verstehen: Auch ohne return werden die Seiteneffekte ausgeführt. Variablen, die innerhalb von eval() erzeugt werden, existieren danach im aktuellen Scope. Das macht den Code schwer nachvollziehbar, weil Variablen scheinbar aus dem Nichts auftauchen.
Grundlegende Beispiele
In den folgenden Beispielen wird gezeigt, wie eval() Strings als PHP-Code interpretiert:
<?php
/* Beispiel 1: Variable dynamisch erzeugen */
$variable_name = 'alter';
$wert = 30;
eval('$' . $variable_name . ' = ' . $wert . ';');
echo $alter; // 30
/* Beispiel 2: Dynamischer Funktionsaufruf */
$code = 'echo strtoupper("hallo welt");';
eval($code); // HALLO WELT
/* Beispiel 3: Komplexerer Code */
$formel = '$ergebnis = (15 * 3) + 7;';
eval($formel);
echo $ergebnis; // 52
?>
Diese Beispiele zeigen die Grundidee. In der Praxis führt dieser Ansatz jedoch zu schwer wartbarem Code, weil der tatsächlich ausgeführte Code erst zur Laufzeit feststeht. Statische Analyse-Tools wie PHPStan oder Psalm können solchen Code nicht prüfen und warnen daher bei jeder Verwendung von eval().
Warum eval() gefährlich ist
eval() führt beliebigen PHP-Code aus. Wenn auch nur ein Teil des übergebenen Strings von außen beeinflusst werden kann, hat ein Angreifer die volle Kontrolle über den Server. Das ist keine theoretische Gefahr, sondern ein reales Sicherheitsproblem, das regelmäßig ausgenutzt wird.
Ein konkretes Exploit-Szenario
Stell dir vor, ein Entwickler baut einen einfachen Online-Taschenrechner. Der Benutzer gibt eine Formel ein, und eval() berechnet das Ergebnis:
<?php
/* UNSICHERER Code: Niemals so verwenden! */
$formel = $_GET['formel'];
eval('$ergebnis = ' . $formel . ';');
echo "Ergebnis: $ergebnis";
?>
Der Entwickler denkt: Die Benutzer geben Formeln wie 5+3 oder 10*2 ein. Was aber, wenn ein Angreifer diesen Wert sendet?
/* Angreifer sendet als GET-Parameter: */
formel=1; system('cat /etc/passwd')
/* Der eval()-Aufruf wird dann zu: */
eval('$ergebnis = 1; system("cat /etc/passwd");');
/* Ergebnis: Der Angreifer kann beliebige
Systembefehle ausfuehren und z.B.
Passwoerter, Dateien oder Datenbanken
auslesen */
Mit einem einzigen manipulierten Parameter kann der Angreifer beliebige Systembefehle ausführen, Dateien lesen, die Datenbank manipulieren oder eine Backdoor installieren. Selbst eine Whitelist für erlaubte Zeichen (nur Zahlen und Operatoren) ist fehleranfällig, weil PHP viele kreative Wege bietet, Einschränkungen zu umgehen.
Error-Handling: ParseError seit PHP 7
Was passiert, wenn der String ungültigen PHP-Code enthält? Seit PHP 7.0 wirft eval() eine ParseError-Exception. In älteren PHP-Versionen gab die Funktion stattdessen false zurück.
<?php
try {
eval('das ist kein gueltiger PHP-Code');
} catch (ParseError $e) {
echo "Syntaxfehler: " . $e->getMessage();
// "syntax error, unexpected identifier
// "ist""
}
?>
Das geänderte Verhalten ist ein Vorteil: In modernem PHP kann man Syntaxfehler in eval() sauber abfangen. Aber: Logische Fehler oder gefährlicher Code werden dadurch nicht verhindert. Ein syntaktisch korrekter system('rm -rf /')-Aufruf löst keinen ParseError aus.
Verwandte gefährliche Funktionen
eval() ist nicht die einzige Funktion mit diesem Risikoprofil. Zwei verwandte Funktionen tauchen häufig in älterem PHP-Code auf:
- assert(): Bis PHP 7.1 konnte
assert() einen String als PHP-Code ausführen, genau wie eval(). Ab PHP 7.2 ist dieses Verhalten veraltet, ab PHP 8.0 werden nur noch echte Expressions akzeptiert. In Legacy-Code vor PHP 7.2 ist assert() mit String-Argumenten genauso gefährlich wie eval(). - create_function(): Diese Funktion war ein Wrapper um
eval() und erzeugte anonyme Funktionen aus Strings. Seit PHP 7.2 veraltet, seit PHP 8.0 komplett entfernt. Der Ersatz sind echte Closures (anonyme Funktionen mit dem function-Schlüsselwort).
Sichere Alternativen zu eval()
Für fast jeden Anwendungsfall von eval() gibt es eine sicherere Lösung. Hier die häufigsten Muster und ihre Alternativen:
Variable Funktionsaufrufe statt eval()
Wenn der Name einer Funktion dynamisch ist, braucht man kein eval(). PHP unterstützt variable Funktionsnamen und call_user_func():
<?php
/* Statt eval(): */
$func = 'strtoupper';
eval('$result = ' . $func . '("hallo");');
/* Besser: Variable Funktion */
$func = 'strtoupper';
$result = $func('hallo'); // HALLO
/* Oder mit call_user_func(): */
$result = call_user_func('strtoupper', 'hallo');
?>
Variable Klassen statt eval()
Auch Klassennamen können in PHP dynamisch sein:
<?php
/* Statt eval(): */
eval('$obj = new ' . $className . '();');
/* Besser: Variable Klasse */
$obj = new $className();
/* Oder mit Reflection: */
$ref = new ReflectionClass($className);
$obj = $ref->newInstance();
?>
Closures statt dynamischem Code
Anonyme Funktionen (Closures) ersetzen dynamische Codeblöcke sicher und lesbar:
<?php
/* Statt eval() mit create_function(): */
$add = create_function('$a, $b', 'return $a + $b;');
/* Besser: Closure (seit PHP 5.3) */
$add = function($a, $b) {
return $a + $b;
};
/* Oder als Arrow-Function (seit PHP 7.4): */
$add = fn($a, $b) => $a + $b;
echo $add(3, 4); // 7
?>
match statt eval() für bedingte Logik
Seit PHP 8.0 kann match viele Fälle ersetzen, in denen eval() für bedingte Zuweisungen missbraucht wurde:
<?php
$operation = 'multiply';
/* match statt eval() */
$result = match($operation) {
'add' => 10 + 5,
'subtract' => 10 - 5,
'multiply' => 10 * 5,
'divide' => 10 / 5,
default => throw new InvalidArgumentException(
"Unbekannte Operation: $operation"
),
};
echo $result; // 50
?>
eval() in Legacy-Code: Migrations-Guide
Wer eval() in bestehendem Code findet, sollte es Schritt für Schritt ersetzen. Die folgenden Muster kommen am häufigsten vor:
- Dynamische Formeln auswerten: Statt
eval('return ' . $formel . ';') eine sichere Bibliothek wie symfony/expression-language verwenden. Diese wertet mathematische und logische Ausdrücke aus, ohne beliebigen PHP-Code zu erlauben. - Dynamische Klassennamen: Statt
eval('new ' . $class . '()') die variable Klassen-Syntax new $class() verwenden. Bei Bedarf vorher mit class_exists() prüfen. - Template-Strings mit eingebettetem PHP: Statt
eval() in Template-Strings eine echte Template-Engine wie Twig oder Blade verwenden. Diese trennen Logik von Darstellung und verhindern Code-Injection. - Konfiguration als PHP-Code: Statt
eval(file_get_contents('config.php')) die Datei per include() einbinden oder besser auf JSON- bzw. YAML-Konfigurationen umsteigen.
Server-Absicherung mit disable_functions
Eine häufige Frage: Kann man eval() in der php.ini per disable_functions sperren? Die Antwort ist nein. Da eval() ein Sprachkonstrukt und keine Funktion ist, greift disable_functions hier nicht.
Was man aber tun kann: Verwandte gefährliche Funktionen sperren, die ein Angreifer über eval() aufrufen könnte:
; php.ini
disable_functions = exec, system, passthru,
shell_exec, popen, proc_open
Damit kann ein Angreifer selbst bei einem erfolgreichen eval()-Exploit keine Systembefehle ausführen. Das ist eine zusätzliche Sicherheitsschicht, ersetzt aber nicht die Entfernung von eval() aus dem Code.
Statische Analyse-Tools wie PHPStan und Psalm warnen standardmäßig bei jeder Verwendung von eval(). In einer CI/CD-Pipeline können solche Warnungen als Fehler konfiguriert werden, sodass neuer Code mit eval() gar nicht erst eingecheckt werden kann.
Zusammenfassung
eval() führt Strings als PHP-Code aus und ist damit eines der größten Sicherheitsrisiken in PHP. Sobald Benutzereingaben auch nur indirekt in den eval()-String gelangen, kann ein Angreifer beliebigen Code auf dem Server ausführen. In modernem PHP gibt es für jeden Anwendungsfall sichere Alternativen: variable Funktions- und Klassennamen, Closures, match-Expressions und Bibliotheken wie symfony/expression-language. Wer eval() in Legacy-Code findet, sollte es systematisch durch diese Alternativen ersetzen. Als zusätzliche Absicherung empfiehlt sich das Sperren von System-Funktionen per disable_functions in der php.ini.