Nachdem ich vor knapp 3 Wochen zufällig festgestellt habe, dass über das PHP-Webinterface von einem der größten deutschen Web-Hoster nahezu alle Rechnungen, unter anderem inkl. Kunden-Adressen, gekürzten Bankverbindungen, Domains sowie IP-Adressen öffentlich abrufbar sind, habe ich mich entschieden, einen kurzen Beitrag über die Absicherung von Web-Anwendungen zu schreiben. Zur Ausnutzung der Lücke war keinerlei technisches Fachwissen erforderlich - das Skript zum Download der Rechnung hat einfach jede beliebige Rechnung ungeprüft ausgeben. In der URL musste nur die gewünschte Rechnungsnummer eingegeben werden. Aufgefallen ist es mir, nachdem ich bemerkt hatte, dass ich eine Rechnung von meinem Zweitaccount beim Anbieter herunterladen konnte, ohne dass ich dort noch angemeldet war.
Problem 1: Keine Authentifizierung
Oftmals finde ich Anwendungen, die zwar einen Login haben, dieser jedoch nur den Zweck hat, auf einen geschützten Bereich weiterzuleiten. Beim Öffnen einer Seite in einem geschützten Bereich wird nicht erneut geprüft, ob der User authentifiziert ist. Dabei lässt sich dies unglaublich leicht realisieren:
<?php session_start(); if (isset($_SESSION['username'])) { --> Passt } else { --> Zur Anmeldung weiterleiten } ?>
Geschützter Content sollte niemals (!) ohne Authentifizierung ausgeführt werden.
Problem 2: Keine Autorisierung
Neben der Authentifizierung sollte stets geprüft werden, ob der User auch autorisiert ist, die gewünschte Aktion auszuführen. Im Beispiel einer Rechnungsabfrage über SQL darf niemals die Anfrage ungeprüft über SQL ausgeführt werden. Die SQL Anfrage sollte soweit eingeschränkt sein, dass ein User nur auf Daten zugreifen kann, die für ihn bestimmt sind.
Problem 3: Unverschlüsselte Übertragung
Ein geschützter Bereich bringt wenig, wenn die Anmeldung im Klartext durchgeführt wird. Es kann nur in den seltensten Fällen sichergestellt werden, dass die Daten unterwegs nicht ausgelesen werden können. Einen HTTP Request mit Wireshark auszulesen, bedarf keines großen Könnens. Die Thematik wird umso schwerwiegender, wenn im Unternehmen Single-Sign-On (SSO) bzw. LDAP verwendet wird.
Problem 4: Ungeprüfte Übernahme von Kundeneingaben
Kundeneingaben sind stets zu prüfen. Kundeneingaben dürfen niemals ungeprüft verarbeitet werden. Im Beispiel einer SQL Injection:
<?php $query = "UPDATE users SET password='$_GET["pwd"]' WHERE username='$_GET["user"]';"; $result = mssql_query($query); ?>
Die Überraschung wird dann groß, wenn ein Users in der URL nicht seinen eigenen Usernamen, sondern z.B. "%admin%" übergibt. Oder nach einem Semikolon einen weiteren SQL Befehl anhängt, DROP DATABASE zum Beispiel.
Zur Vermeidung bietet mysql_real_escape_string() oder sqlite_escape_string() eine gute erste Anlaufstelle.
Problem 5: Keine Tokens verwenden
Im Beispiel eines Online-Shops: Bei der Absendung der Bestellung wird mittels PHP ein neuer Datenbankeintrag für die Bestellung erzeugt. In vielen Fällen kann die URL beliebig oft ausführt - und die Datenbank mit immer neuen Einträgen gefüllt werden. Generieren Sie hierbei Tokens - eine zufälligen Zeichenkette, die bei der Ausführung eines Skriptes geprüft und anschließend gelöscht wird. Ein weiterer Einsatzort von Tokens wären z.B. Kontaktformulare.
Problem 6: $_POST als Sicherheitsfeature
Um Daten an einen Server zu übertragen, wird aus "Sicherheitsgründen" gerne POST anstelle von GET verwendet. Die Nutzereingabe taucht somit nicht mehr in der URL auf. Mehr Sicherheit bietet diese Methode jedoch nicht. In Firefox oder Chrome können diese Daten sehr leicht ausgelesen und manipuliert werden.
Problem 7: Unsichere Konfiguration des Webservers oder PHP
Beschäftigen Sie sich sorgfältig mit der Konfiguration ihres Servers. Prüfen Sie Ihre Einstellungen. Verwalten Sie den Server selbst, denken Sie z.B. über Sicherheitsframeworks wie AppArmor oder GRADM nach.
Problem 8: Klartextpasswörter speichern
Zu Beginn: Datensparsamkeit. Überlegen Sie sich stets, ob sie diese oder jene Daten wirklich abspeichern müssen. Im Fall von Passwörtern: Niemals im Klartext, auch nicht als MD5- oder SHA1-Hash. Verwenden Sie sicherere Funktionen: Salted BCrypt beispielsweise.
Problem 9: Kein Vorwissen über mögliche Schwachstellen
Wenn Sie eine PHP Anwendung entwickeln bzw. warten müssen, ist es empfehlenswert, sich bereits zu Beginn über gängige Schwächen zu informieren. Informieren Sie sich etwa über SQL Injections, Cross-Site-Scripting, Cross-Site-Request-Forgery oder das Erraten von URLs.
Problem 10: Session Hijacking
Seien Sie sich über die Problematik des Session Hijackings bewusst. Versuchen Sie die Problematik zu erschweren: Speichern Sie Beispielsweise die Source-IP Adresse des Users in der PHP Sitzung ab und validieren Sie diese.
Problem 11: Keine automatisch ablaufenden Sitzungen
Stellen Sie sicher, dass ein Nutzer nach einer bestimmten Inaktivitätszeit automatisch ausgeloggt wird, insofern dies möglich ist.
Es gibt enorm viele Möglichkeiten PHP Anwendungen zu sichern, was mit einigem Aufwand verbunden sein kann. Die genannten Punkte sind ein Anfang, ein Mindestschutz, der in der Praxis leider immer noch häufig nicht berücksichtigt wird.
Die von mir gefundene Lücke wurde durch den Anbieter nach meiner Meldung innerhalb eines Werktages geschlossen.
Bildnachweise:
- © timkaekler - Fotolia.com