Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Script Mods
 phpBB Adsense Mode

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 TLD Liste
 Webkatalog‑Verzeichnis

Partner
 Sprüche Treff

Artfiles.de
Bietet Serviceorientierte...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains...
https://www.Hosterplus.de
 
 
 

Dynamisch zusammengesetzten PHP Code ausführen

Sie befinden sich: Home > Php Tutorial > Dynamisch...

Dynamisch zusammengesetzten PHP Code ausführen


Eintrag am:  25.12.2022
Hits / Besucher:  1385
Sprache:  Deutsch
Kategorie:  Einsteiger Tutorials
Tutorial Art:  eigenes
Eingetragen von   schubertmedia schubertmedia
 
Beschreibung

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.

 

Tags:

 

Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.