Grundsicherung für PHP-Software

Private Blogs und Foren sind ein beliebtes Angriffsziel -- auch wegen der vielen Sicherheitslücken darin. Wer sich jedoch nicht mit den unsicheren PHP-Voreinstellungen seines Providers zufrieden gibt, kann dem meisten Ärger aus dem Weg gehen.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 12 Min.
Von
  • Christiane Rütten
Inhaltsverzeichnis

PHP gilt gemeinhin als unsichere Programmiersprache. Täglich liest man neue Meldungen über Schwachstellen in PHP-Programmen, sodass Hobby-Admins mit dem Einspielen von Patches und neuen Versionen kaum nachkommen. Doch inzwischen gibt es in der verbreiteten Skriptsprache durchaus ein ganze Reihe von Sicherheitsmechanismen, die dafür sorgen können, dass sich Lücken nicht ausnutzen lassen oder zumindest kein größerer Schaden resultiert.

Das Problem ist jedoch, dass die Sicherheitsoptionen aus Kompatibilitätsgründen in der PHP-Standardkonfiguration allesamt deaktiviert sind. Jeder der Mechanismen geht mit speziellen Einschränkungen oder besonderen Programmiervorgaben einher [1]. Weil aufgrund der vorherrschenden laxen PHP-Sicherheitskonfiguration der Webserver kaum ein Web-Entwickler dazu gezwungen ist, beispielsweise die Einschränkungen des Safe-Mode zu berücksichtigen, ist die PHP-Welt voller Applikationen, die auf abgesicherten Servern Probleme machen oder sogar den Dienst komplett verweigern.

Shared-Webhosting-Provider können es sich nicht leisten, die Sicherheitsschraube für alle Serverkunden gleichsam anzuziehen. Dies hätte massenweise Support-Anfragen wegen nicht funktionierender Webapplikationen zur Folge. Die leidige Konsequenz ist, dass im Shared-Webhosting-Bereich keine nennenswert gesicherten PHP-Umgebungen anzutreffen sind. Wir haben uns exemplarisch fünf Angebote angesehen, und keiner der untersuchten Provider traute sich, wenigstens die wichtigste Sicherheitsoption register_globals = off zu setzen. Damit ist der Teufelkreis komplett, denn so entsteht wiederum kein Druck auf die Entwickler, ihre Anwendungen an den Sicherheitsmechanismen ausrichten zu müssen.

Auf serverseitige Sicherungsmaßnahmen wie etwa den Einsatz der PHP-Erweiterung Suhosin [2] hat man als Webspace-Kunde keinen Einfluss. Aber es gibt auch nutzerseitige Möglichkeiten, die PHP-Konfiguration zu beeinflussen. Wie dafür vorzugehen ist und wie weit der Einfluss geht, hängt jedoch wieder von der Serverkonfiguration des Providers ab - und dort herrscht ein ausgeprägter Wildwuchs (siehe Tabelle).

Bei den untersuchten Webspace-Angeboten kommt durchweg der Webserver Apache zum Einsatz, doch allein bei ihm gibt es zwei grundverschiedene Arten, PHP einzubinden: als Apache-Modul mod_php sowie über den althergebrachten CGI-Mechanismus. Einen brauchbaren Zustandsbericht über den eigenen Webspace liefert ein kurzes PHP-Skript mit dem Inhalt

<?php phpinfo(); ?>

Steht in der Skriptausgabe im Feld "Server API" der Wert "Apache Handler", läuft mod_php, andernfalls steht dort "CGI". Weitere Informationen über den Webspace, etwa die User-ID, lassen sich beispielsweise mit Zusatzprogrammen wie der PHPShell abfragen.

Mit diesem Wissen gerüstet, kann man daran gehen, seinen Webspace abzusichern.

Der von den meisten Hostern eingesetzte CGI-Ansatz ist flexibel und richtig konfiguriert auch recht sicher. Dabei laufen PHP-Anwendungen über den klassischen CGI-Mechanismus als eigener Prozess. Damit die Skripte dabei dann nicht mit vollen Server-Rechten sondern mit User-Rechten laufen, setzen die Provider die Apache-Module mod_suexec oder mod_suphp ein. Das schützt nicht nur den Server selbst vor den Konsequenzen von Einbrüchen, sondern auch die anderen Servernutzer.

Weiterhin hat der Web-User bei einer CGI-Konfiguration nahezu unbegrenzte Einflussmöglichkeiten auf die PHP-Optionen. Denn der PHP-Interpreter verwendet im CGI-Modus bevorzugt eine php.ini-Datei, die im selben Verzeichnis wie das aufgerufene PHP-Skript liegt.

Eine php.ini gilt anders als .htaccess-Dateien allerdings nicht für Unterverzeichnisse. Daher gehört in jedes Verzeichnis mit PHP-Skripten, die sich direkt aufrufen lassen, eine eigene php.ini-Datei. Bei reinen Include-Verzeichnissen – wie etwa dem wp-content-Ordner des Blog-Systemes Wordpress – ist das nicht nötig. Im Zweifel sollte man jedoch lieber auf Nummer sicher gehen und in jedes Verzeichnis eine php.ini kopieren. Wenn manuelles Kopieren zu aufwendig ist, kann das kleine Perlskript ftp_spread.pl behilflich sein. Es kopiert eine beliebige Datei in jedes Unterverzeichnis eines anzugebenden FTP-Pfades.

Bevor man sich an die Arbeit macht, passende php.ini-Dateien zu erstellen, hier ein kurzer Überblick über die wichtigsten Sicherheitsoptionen von PHP:

  • allow_url_fopen (empfohlen: off) steuert, ob Dateizugriffe auch auf externe URLs verweisen dürfen oder nicht. Verbietet man dies, fällt es einem Angreifer schwerer, Schadprogramme aus dem Internet nachzuladen. Seit PHP 5.2.0 regelt allow_url_include das Verhalten separat für die Befehle include() und require().
  • display_errors (empfohlen: off) schaltet die Anzeige von PHP-Fehlermeldungen ein oder aus. Was fürs Debugging hilfreich ist, kann andererseits auch Angreifern Aufschluss über Anwendungsinterna geben, die für weitere Attacken hilfreich sind.
  • disable_functions spezifiziert eine Liste von gesperrten PHP-Funktionen. Insbesondere PHP-Anwendungen, die unter Sicherheitsapekten entwickelt wurden, machen einen großen Bogen um potenziell gefährliche Funktionen wie exec(), sodass ihr Fehlen ihnen häufig keine Probleme bereitet -- vielen im Umlauf befindlichen Exploits aber sehr wohl.
  • open_basedir (empfohlen: Web-Home) limitiert die Dateioperationen von PHP auf das angegebene Verzeichnis und darin enthaltene Unterverzeichnisse. Es ist möglich, mehrere Verzeichnisse mit einem Doppelpunkt getrennt anzugeben. Wichtig ist, Pfadangaben mit einem Slash zu beenden, da sie andernfalls auch alle Verzeichnisse einschließen, die mit dem angegebenen Namen beginnen.
  • register_globals (empfohlen: off) steuert, ob Skripte Parameter aus der URL oder den POST-Daten als globale Variablen übergeben bekommen oder nicht. Viele Schwachstellen in PHP-Anwendungen lassen sich nur ausnutzen, wenn dies der Fall ist.
  • safe_mode (empfohlen: on) gibt an, ob PHP in einem speziellen abgesicherten Modus laufen soll oder nicht. Er hat weitreichende Auswirkungen, beispielsweise findet bei Dateioperationen ein zusätzlicher UID-Check statt und der Zugriff auf Umgebungsvariablen ist eingeschränkt. PHP6 wird ihn nicht mehr enthalten, weil die Entwickler der Ansicht sind, dass seine Funktionen nicht im Aufgabenbereich einer Skriptsprache liegen. Auf der PHP-Seite ist er umfassend dokumentiert [3].
  • sql.safe_mode regelt eine Sonderbehandlung von Anmeldungen an Datenbankservern. In diesem Modus verwenden die PHP-Funktionen zur Datenbankanmeldung ausschließlich den Namen des System-Users, dem das Skript gehört. Da in Shared-Webhosting-Umgebungen der Name des Datenbanknutzers fast nie mit dem es System-Users übereinstimmt, kommt dort diese Option nur äußerst selten in Frage.

Eine besonders abgedichtete php.ini hat damit den folgenden Inhalt:

[PHP]
register_globals = off
allow_url_fopen = off
safe_mode = on
open_basedir = <Pfadname des Webverzeichnisses>
disable_functions = exec,system,passthru,shell_exec,popen,escapeshellcmd,proc_open,proc_nice,ini_restore
display_errors = off

Einige dieser Optionen sind noch anzupassen. Für open_basedir ist der Pfad zum Web-Root einzutragen, wie er beispielsweise in der PHP-Variablen DOCUMENT_ROOT der Ausgabe von phpinfo() zu lesen ist. Sind mehrere Applikationen in verschiedenen Unterverzeichnissen mit eigenen php.ini-Dateien installiert, ist es empfehlenswert, jeweils den Basispfad der zu schützenden Web-Anwendung einzutragen.

Eher die Ausnahme sind Angebote wie die von Server4u und all-inkl, die auf die Einbindung mit mod_php setzen. Sie ermöglicht Webspace-Kunden eine komfortable aber rudimetäre PHP-Konfiguration durch .htaccess-Dateien im Webverzeichnis. Mit solchen .htaccess-Dateien lassen sich nur zwei der wichtigen Sicherheitsoptionen von PHP beeinflussen. Eine im Web-Root abgelegte .htaccess-Datei mit dem Inhalt

php_flag register_globals off
php_flag display_errors off

ist neben dem Absichern durch eine reguläre HTTP-Authentifizierung im daher Prinzip schon alles, was man bei der mod_php-Variante selbst tun kann. Wie bei .htaccess-Dateien üblich gelten die dort getroffenen Einstellungen auch für alle Unterverzeichnisse.

Bei diesem Ansatz laufen allerdings alle PHP-Skripte mit der User- und Gruppen-ID des Webserverprozesses. Da er für den Zugriff auf die Webverzeichnisse weitreichende Lesebefugnisse haben muss, kann das im Falle eines Einbruchs erhebliche Konsequenzen für die anderen Serverbewohner oder gar den Server selbst haben.

Hier treffen die Provider jedoch in der Regel andere Schutzmaßnahmen, etwa eine ausgefeilte Vergabe von Zugriffsrechten und sogenannte chroot-Jails. all-inkl beschränkt PHP-Dateizugriffe beispielsweise mit der Einstellung open_basedir auf das Kunden-Verzeichnis.

Die folgende Tabelle gibt nochmal einen Überblick wo man welche Sicherheitsoptionen setzen kann:

Sicherheitsoption PHP-Default php.ini .htaccess
allow_url_fopen on ja nein
allow_url_include1 off ja nein
display_errors on ja ja
open_basedir NULL ja nein
register_globals on ja ja
safe_mode off ja nein
sql.safe_mode off ja nein
1 ab PHP 5.2.0

Bei einer stark einschränkenden php.ini kann es zu Problemen kommen. Mit allow_url_fopen = off kann beispielsweise das Forensystem phpBB keine Avatare mehr von externen Servern nachladen. Daher sind möglichst umfangreiche Funktionstests mit einem vorübergehend gesetzten display_errors = on unentbehrlich. Bei Störungen oder nicht hinnehmbaren Funktionsbeschränkungen müssen in der zugehörigen php.ini die Restriktionen gelockert werden. Geben die Fehlermeldungen keinen Aufschluss über das Problem, hilft nur das Experimentieren mit den einzelnen Optionen.

Der Installer der kommenden phpBB-Version 3 gibt vorbildliche Erläuterungen zu den angetroffenen Sicherheitseinstellungen.

Bei einigen modernen PHP-Anwendungen liefert aber bereits die Installationsroutine Hinweise auf mögliche Probleme durch zu laxe oder feste Daumenschrauben. Beispielsweise fragt die kommende Version 3 von phpBB einige PHP-Sicherheitsoptionen ab und gibt entsprechende Hinweise. Das Content-Management-System Joomla hingegen spuckt bei register_globals = on eindeutige Warnhinweise zur unsicheren Serverkonfiguration aus.

In Shared-Webhosting-Umgebungen verdienen Dateien mit Zugangsdaten und Passwörtern ein besonderes Augenmerk. Die meisten Webanwendungen mit MySQL-Anbindung legen ihre Datenbankpasswörter im Klartext im Webverzeichnis ab. Bei Wordpress heißt die Datei beispielsweise wp-config.php, bei phpBB config.php. Im Idealfall sollten diese Dateien nur für den Besitzer lesbar sein, was sich mit den meisten FTP-Programmen über die Dateieigenschaften festlegen lässt.

Das Content-Management-System Joomla warnt vor unsicherer Serverkonfiguration.

Doch bei all-inkl und Server4u ist es erforderlich, dass sowohl PHP-Dateien als auch Include-Dateien Leserechte für alle Nutzer haben müssen. Andernfalls liefert der Server Fehlermeldungen oder ein leeres Dokument an die Web-Browser aus. Zwar ist es uns bei beiden Providern nicht gelungen, auf fremde Web-Homes zuzugreifen und an die Datenbankpasswörter zu gelangen, doch solche Dateien mit vollen Leserechten auzustatten, hinterlässt ein ungutes Gefühl.

Wer besonders sicher gehen will, kann bei php.ini-Dateien und den Verzeichnissen, in denen sie liegen, auch die Schreibrechte entfernen und mit Hilfe von disabled_functions dafür sorgen, dass PHP-Skripte nicht mit chmod() an den Zugriffsrechten drehen können. Ein gekapertes PHP-Programm könnte die Dateien dann nicht mehr mit eigenen Werten füttern oder löschen und so die Sicherheitsvorkehrungen aushebeln.

Wer funktionelle Einschränkungen oder Fehlfunktionen vermeiden möchte, darf bei den Sicherheitsvorkehrungen nicht übertreiben.

Allerdings müssen die Schreibrechte etwa zum Einspielen von Updates vorübergehend wieder zurückgedreht werden. Diese Maßnahme ist sehr zeitaufwendig und fehleranfällig und daher in der Regel nicht die Mühe wert. Wordpress beschwert sich bei deaktiviertem chmod() zum Beispiel, dass es Zugriffsrechte von Dateien und Upload-Verzeichnissen nicht automatisch anpassen kann, was noch mehr Handarbeit für den Hobby-Admin bedeutet.

Auch die gezeigten User-seitigen Mechanismen können nicht jeden Angriff vereiteln. Insbesondere entbinden sie den Webspace-Nutzer nicht von der Pflicht, sich über Aktualisierungen seiner PHP-Anwendungen auf dem Laufenden zu halten und regelmäßig Updates einzuspielen. Wenn schludrige PHP-Skripte Nutzereingaben unzureichend gefiltert an die SQL-Datenbank durchreichen, können Angreifer beziehungsweise Schadprogramme trotz aller Absicherung an den Anwendungsdaten herummanipulieren.

Außerdem lässt sich allow_url_fopen = off unter Umständen mit Hilfe sogenannter Streams ("php://input" und "data://") umgehen. Lediglich die Suhosin-Erweiterung kann das unterbinden. Ist dem Angreifer das Datenbankpasswort in die Hände gefallen, kann er möglicherweise auch über den MySQL-Server Dateien außerhalb des open_basedir auslesen. Ebenfalls ist zu beachten, dass PHP-Beschränkungen nicht für externe Programme und andere Skriptsprachen gelten.

Allerdings bringen schon die wenigen aufgeführten Schritte eine Web-Anwendung aus der Schusslinie der täglich tausendfach einprasselnden Webattacken. Hinter fast allen schädlichen Zugriffen stecken automatisierte Angriffsprogramme, die vornehmlich auf die Standardkonfigurationen zielen und nur äußerst selten versuchen, besondere Sicherheitsmaßnahmen zu umgehen. Denn auch ohne aufwendige Tricks finden Bösewichte mit ihren Massen-Exploits genügend viele verwundbare Web-Applikationen, die sie für ihre Zwecke missbrauchen können.

Diese Tabelle gibt einen Überblick über die PHP-Konfiguration der Webspace-Angebote 1&1 Homepage Perfect, all-inkl Privat, Goneo Homepage Premium, Server4u Racer, Strato Powerweb A und wie man einen eigenen Root-Server einrichten kann.

[1] Christiane Rütten, Tobias Glemser, Gesundes Misstrauen, Sicherheit von Webanwendungen, c't 26/06, S. 234

[2] Informationen zur PHP-Sicherheitserweiterung Suhosin

[3] Dokumentation des PHP-Safe-Mode

[4] Offizielle PHP-Dokumentation zum Thema Sicherheit

[5] Tipps zur php.ini-Konfiguration

Die folgenden Daten wurden im August 2007 ermittelt.

Paket 1&1 Homepage Perfect all-inkl Privat Goneo Homepage Premium Server4u Racer Strato Powerweb A abgesicherter Rootserver1
Apache-Version 1.3.33 2.x2 2.0.59 2.0.48 1.3.31 2.2.3
PHP-Version 4.4.7 4.4.6 4.4.6 4.4.6 4.4.7 5.2.0
Server-Schnittstelle CGI / suexec mod_php CGI / suphp mod_php CGI / suexec CGI / suphp
Suhosin-Erweiterung nein ja nein nein nein ja
PHP-Privilegien User Server User Server User User
Individuelle Datei-GID nein ja nein nein nein ja
AuthConfig mit .htaccess ja ja ja ja ja ja
HTTP-Auth mit PHP nein ja ja ja nein ja
PHP-Config mit .htaccess nein ja nein ja nein nein
eigene php.ini ja nein ja nein ja ja
erforderliche Dateizugriffsrechte User Alle User Alle User User
PHP-Defaults
allow_url_fopen on on on on on off
disable_functions nein exec system passthru shell_exec popen escapeshellcmd proc_open proc_nice ini_restore nein passthru proc_close proc_get_status proc_nice proc_open proc_terminate shell_exec system apache_note apache_setenv closelog debugger_off debugger_on define_syslog_variables openlog syslog popen pclose ini_restore nein exec system passthru shell_exec popen escapeshellcmd proc_open proc_nice ini_restore
display_errors on on off off on off
open_basedir nein Web-Home, /tmp, Binärverzeichnisse nein Web-Home nein Web-Home
register_globals on on on on on off
safe_mode off on off off off on
1 dient lediglich als Referenz einer besonders strikten Konfiguration, Basissystem Debian-Etch
2 Unterversion ließ sich nicht ermitteln

(cr)