| Fehlermeldungen und Fehlerbehandlung |
Fehlermeldungen, Warnhinweise, Fehlerbehandlung, und mehrUm eine robuste Webanwendung zu programmieren, die möglichst wenig Angriffsfläche für "Hacker" bietet, muss man wissen wie Angreifer vorgehen und wie die Angriffe ablaufen. Man kann Angriffe in zwei Gruppen teilen: 1. Gezielte Angriffe mithilfe eines Exploit 2. Systematische Suche nach Schwachstellen Je mehr Info ein Angreifer über unser System hat, umso eher findet er Ansatzpunkte für seinen Angriff. Informationen die für uns zu Entwicklungszwecke gut und hilfreich sind, haben aber nichts auf einem produktiven, sprich öffentlich zugängliches, System zu suchen. Allgemine Fehlermeldungen und Hinweise auf die Sitestruktur erscheinen uns harmlos, liefern aber Angreifer potentiell gefährliche Informationen, die deren Arbeit erleichtert. Je verschwiegener unser System ist, desto besser. Auf Benutzerfreundlichkeit muss dabei nicht verzichtet werden. Allerdings diktieren wir, und nicht PHP, welche Informationen bei Fehlermeldungen preisgegeben werden! So können wir Sitebesucher über eventuell auftretende Fehler informieren, während keine Informationen heraus gegeben werden, die gegen uns verwendet werden könnten. PHP Error Reporting / FehlermeldungenDas Error Reporting, also die Meldungen die PHP bei Fehlern ausgibt, ist die Wurzel allen Übels. Okay, das klingt etwas hart und stimmt auch nur bedingt. Die vielseitigen und oftmals sehr detaillierten Fehlermeldung sind ein unerlässlicher Helfer bei der Entwicklung einer Webseite, haben aber auf einer Webseite im produktiven Einsatz nichts mehr verloren. Die PHP Funktion, mit der dieses Ausgabeverhalten gesteuert werden kann nennt sich: Als Parameter kann man entweder den numerischen Wert des Level angeben oder die entsprechende PHP-interne Konstante für das gewünschte Level. Momentan gibt es insgesamt 18 Level (Stand PHP 5.3.1), wovon einige Level andere beinhalten. Eine komplette Übersicht findet man wie immer in der Dokumentation zu error_reporting. // Error Reporting unterdrücken error_reporting( 0 ); // Error Reporting auf höchster Ebene anzeigen error_reporting( -1 ); Der Wert -1 stellt das höchste Level der Fehlermeldungen dar, dass alle anderen Level beinhaltet - mehr Fehlermeldung geht also nicht. Fazit für Error ReportingWährend der Entwicklung sollte das Error Reporting eingeschaltet sein und zwar so, dass jegliche Art von Meldung angezeigt wird: error_reporting(-1); Sobald die Webapplikation, bzw. Webseite dann öffentlcih zugänglich gemacht wird um ihren Dienst zu verrichten, sollten alle Meldungen deaktiviert werden: error_reporting(0); Display Errors / FehlerausgabeIm Gegensatz zum Error Reporting, bei dem man festlegen kann welche Art von Fehlermeldung angezeigt werden sollen, dient Display Errors dazu das gesamte Fehlerausgabe-System an-/auszuschalten. Da die Fehlerausgabe tiefer in's System eingreift als das Ausgeben von Fehlermeldungen, muss man hier auch anders vorgehen um Einfluss auf das Verhalten zu nehmen. Möchte man zur Laufzeit des Script die Fehlerausgabe deaktivieren, so gibt es dafür nicht etwa den Befehl display_errors(0), sondern wir müssen direkt die Konfigurationsdatei von PHP ansprechen, die PHP.ini: ini_set( 'display_errors', 'Off' ); Diese Methode hat aber einen Haken. Da der Befehl natürlich erst dann ausgeführt werden kann, wenn das Script abgearbeitet wird, kann es aber bereits vorher schon zu fatalen Fehler kommen, die dann trotzdem ausgegeben werden. display_errors = Off Sollte vor der Zeile ein Semikolon (;) sein, so ist dieses zu entfernen, da es ein Kommentarzeichen ist um die Zeile zu deaktivieren. Anschließend muss der Webserver, in der Regel Apache, neu gestartet werden, bzw. veranlasst werden, die Konfiguration neu zu laden. Sollte man keinen direkten Zugriff auf die PHP.ini haben, so kann man versuchen die Fehlerausgabe via .htaccess zu beeinflussen. Dazu fügt man in die .htaccess Datei diese Zeile hinzu: php_flag display_errors Off Wenn man nun eine Webseite aufruft und es kommt zu einem Fehler, meistens 500 Internal Server Error, dann hat man leider keine Berechtigung diesen Wert per .htaccess zu ändern. In dem Fall hilft nur ein Anfragen an den Provider, ob sie display_errors für einen auf Off setzen können. Wenn man höflich anfragt, sind die Provider meistens bereit dazu. Fazit für Display ErrorsAuf einem Produktiv-System sollte man auf jeden Fall das Anzeigen von Fehlermeldungen komplett deaktivieren. Damit entziehen wir Angreifern quasi die Grundlage für viele Arten von Angriffe: das sammeln von Informationen über unsere Seite. Error Log / Fehlermeldungen protokollierenWenn wir wie empfohlen Error Reporting und Display Errors deaktiviert haben, haben wir aber auch ein kleines Problemchen. Wir wissen nämlich nicht, ob Fehler auftreten und wo Fehler auftreten. Glücklicherweise hat PHP aber auch hier eine Lösung im Angebot. Wir können auftretende Fehler ganz einfach umbiegen, damit diese still und heimlich in ein Log (Protokolldatei) geschrieben werden. So können wir später analysieren, ob Fehler in unserer Anwendung auftreten, wo diese auftreten und ob es sich ggfs. um gezielte Angriffe handelt. log_errors = On error_log = /pfad/zur/php/errorlog.txt Die .htaccess-Variante sieht wie folgt aus: php_flag log_errors On php_value error_log /pfad/zur/php/errorlog.txt Wichtig ist in beiden Varianten, dass die Logdatei bereits existieren muss und Schreibrechte hat, sonst kann es zu unliebsamen Überraschungen kommen! Hat man das Logging aktiviert, kann man auch zur Laufzeit manuell Einträge in die Datei schreiben und sogar per Email verschicken. Dazu verwendet man die Funktion error_log(). Beispiel: // Hängt eine Meldung an die Logdatei error_log( "Es trat ein Fehler auf.\n", 3, "/pfad/zur/php/errorlog.txt" ); // Fehlermeldung per Email senden error_log( "Es trat ein Fehler auf.", 1, "admin@domain.tld", "FROM: Fehlerreport<admin@domain.tld>" ); Fazit für LogdateiEs ist sehr zu empfehlen eine eigene Logdatei zu verwenden, da man so schön kontrollieren und analysieren kann, falls Fehler aufgetreten sind. Wichtig ist natürlich, dass man auch regelmäßig die Logdatei nach Auffälligkeiten durchsuchst und ggfs. sofort aktiv wird, sobald etwas verdächtig erscheint! Benutzerdefinierte FehlerbehandlungBisher haben wir schon einiges geleistet um Angreifer ihre Arbeit zu erschweren indem wir vermeiden, zuviel Informationen über unser System zu verraten und gleichzeitig sorgen wir aber dafür, dass wir selbst Kontrolle über Fehlermeldungen haben. Was dabei auf der Strecke geblieben ist, ist der Besucher unserer Webseite. Wie soll dieser nun wissen, dass Datei xyz nicht gefunden wurde; dass gerade unser Mailsystem ausgefallen ist; das die Datenbank gerade nicht erreichbar ist; usw.? function meineFehlerBehandlung( $fehlercode, $fehlermeldung, $datei, $zeile ) { switch ($fehlercode) { case E_NOTICE: case E_USER_NOTICE: echo 'HINWEIS: In Datei <' . basename( $datei ) . '>, Zeile <' . $zeile . '> wurde folgender Fehler ausgelöst:'; echo $fehlermeldung; break; case E_WARNING: case E_USER_WARNING: echo 'WARNUNG: In Datei <' . basename( $datei ) . '>, Zeile <' . $zeile . '> wurde folgender Fehler ausgelöst:'; echo $fehlermeldung; break; case E_ERROR: case E_USER_ERROR: echo 'FATALER FEHLER: In Datei <' . basename( $datei ) . '>, Zeile <' . $zeile . '> wurde folgender Fehler ausgelöst:'; echo $fehlermeldung; // Weiterer Code der bei einem Fatalen Fehler ausgeführt werden soll, z.B. // Email-Benachrichtigung an Webmaster oder Eintrag in eine Logdatei/Datenbank break; default: echo 'FEHLER: Es ist ein unbekannter Fehler aufgetreten!'; break; } return TRUE; } Der Name der Funktion ist zweitrangig. Wichtig ist hier jedoch, dass sie 4 Parameter verarbeiten können sollte. Der $fehlercode ist das Level des Fehlers, $fehlermeldung ist, na wer errät es?! ;-), $datei und $zeile geben die Datei und Position an in der ein Fehler ausgelöst wurde. set_error_handler ( 'meineFehlerBehandlung' ); Zusätzlich muss man das Error Reporting wieder einschalten: error_reporting(E_ALL) echo $variable; Wird PHP, wegen dem error_reporting(E_ALL), mit einem Hinweis reagieren, aber statt des internen Hinweis wird unser Hinweis (case E_USER_NOTICE) ausgegeben. Wie wir sehen wurde das Fehlverhalten unseres Scripts automatisch von PHP entdeckt und hat darauf reagiert. Wir können aber auch manuell eine PHP-Meldung provozieren: if (file_exists( 'testdummy.txt' ) === FALSE) { trigger_error( 'Kann Datei nicht finden!', E_USER_WARNING ); } Hier prüfen wir ob eine Datei existiert, z.B. weil wir etwas hinein schreiben möchten, und falls die Datei nicht gefunden wurde teilen wir PHP mit, dass es eine Meldung ausgeben soll. Mit trigger_error lösen wir eine Meldung aus (Trigger = Auslöser), die den Typ E_USER_WARNING haben soll und als Meldung wird "Kann Datei nicht finden!" ausgegeben. Noch ein Wort zu den verschiedenen Level einer Fehlermeldung. Wir unterscheiden da:
Fazit für Error HandlerNicht vergessen error_reporting wieder einzuschalten, falls man einen eigenen Error Handler einsetzen möchte! Exceptions / AusnahmebehandlungExceptions, auf Deutsch Ausnahmen, wurden mit PHP 5 eingeführt und stellen im Prizip das Non Plus Ultra dar, was das Abfangen und Verarbeiten von Fehlern betrifft. Dennoch möchte ich ein kurzes Beispiel geben, wie man Exception in der Praxis anwendet und wie diese arbeiten. Zunächst der Code, dann die Erklärung: try { if (copy( 'quelldatei.txt', 'zieldatei.txt' ) === FALSE) { // Fehler wird geworfen, wenn die Datei nicht kopiert werden konnte throw new Exception( 'Die Datei konnte nicht kopiert werden!' ); } } catch (Exception $error) { echo $error->getMessage(); // Eventuell weitere Aktionen zur Fehlerbehandlung } Dies ist ein stark vereinfachtes Beispiel einer Exception. Diese besteht aus zwei Teilen: Voraussetzung ist einmal mehr die deaktivierte Fehlerausgabe von PHP, damit wir uns selbst um Fehler kümmern können. Im try-Block führen wir also Code aus, der einen Fehler verursachen könnte, der die weitere Verarbeitung des Scripts beeinträchtigen kann, z.B. herstellen einer Datenbankverbindung. Schlägt der Verbindungsaufbau fehl, brauchen wir auch nicht zu versuchen Daten aus der DB auszulesen und ebenso brauchen wir kein Template laden, durch das wir die Daten aus der DB ausgeben wollten. Als Erklärung belasse ich es dabei, da das Thema zu komplex ist, als das ich hier in die Tiefe gehen könnte. Weitere Beispiele für den praktischen Einsatz von Exception finden sich in diversen Klassen von mir, wie etwa in der Datenbank-Klasse vom Gästebuch oder auch in der FTP Klasse. Fazit für ExceptionsWer programmiertechnisch schon so weit ist, dass er OOP beherrscht und Exceptions versteht, für den gibt es meistens nichts besseres als Fehlerbehandlung. Besonders in Verbindung mit den weiter oben vorgestellten Möglichkeiten bekommt man von PHP ein sehr mächtiges und flexibles Werkzeug in die Hand, mit dem man bei auftretenden Fehlern reagieren kann, seine Site-Besucher nicht im Dunkeln stehen lässt und Angreifern den Zugang zu Informationen erheblich erschwert. Fazit und DownloadDer Erste Teil dieser Reihe war ziemlich theoretischer Natur, aber es ist sehr wichtig zu verstehen, wie Angriffe ablaufen und das diese quasi ausnahmslos damit beginnen, dass zunächst Informationen über ein System gesammelt werden. Indem man der Freizügigkeit, in Bezug auf Systeminformationen, von PHP entgegen wirkt, kann man sich bereits sehr viel Ärger ersparen. Ohne Informationen ist es für einen Angreifer schwer ein Einfallstor zu finden - oder wo steigt ein Einbrecher eher ein: in ein Haus das weder Fenster noch Türen hat oder in ein Haus, dass ringsum aus Türen und Fenster besteht, wovon vielleicht ein Fenster auf kipp steht? ;-) Kommen wir zur Frage, für wen sich was auf einem Produktiv-System eignet:
So weit mal eine kleine Einführung in die Thematik. Aus Platzgründen kann ich leider nicht auf alle Feinheiten eingehen, auch wenn der Artikel schon recht lang wurde. Anwendungsbeispiele (1 KB) |