Henryk Plötz: Sichere Scripte schreiben.

Beitrag lesen

Moin

Punkt 1: 'Input'-Daten auf Gültigkeit überprüfen.

* Daten auf formale Richtigkeit prüfen.
* Daten die an externe Programme/die Shell/ oder kritische Funktionen ('eval();') gehen auf ungültige Zeichenfolgen prüfen. (z.B. '|' bei Daten an Sendmail) Bzw. prüfen das nur gültige Zeichenfolgen vorkommen.
* Wenn es sich um Pfadangaben zu Dateien/Verzeichnissen handelt prüfen ob nicht unerlaubterweise versucht wird den Dateipfad zu wechseln. Bsp. '../../../etc' o.ä.

Das Prüfen der Daten ist sicherlich sehr wichtig. Bei mir laufen Skripte in der Regel nicht weiter ohne das alle übergebenen Daten überprüft wurden. Wenn dabei was grob böses passiert, also etwas das auf absichtliche Manipulation schließen lässt, wird der Browser auf eine ungefährliche Seite (meist index.php) umgeleitet und das Skript beendet. Bei einfachen Fehlern, wenn der Benutzer Angaben vergessen hat oder so, bekommt der Benutzer eine Fehlermeldung und erhält die Chance seine Daten zu korrigieren.

Im Übrigen benutze ich die Magic Quotes von PHP. Am Anfang aller Skripte wird eine Datei includiert die get_magic_quotes_gpc() abfragt und das escapen gegebenenfalls nachholt, falls der Admin diese Option in der Konfigurationsdatei abgeschaltet haben sollte. Danach kann ich mich darauf verlassen, dass keine Variable mehr ein unescaptes " oder ' enthält. Wenn man also im Folgenden alle Aufrufe von externen Programmen richtig macht - alle übergegebenen Parameter in Anführungszeichen setzen - sollte es keine Probleme mehr geben, auch mit den ausgefallensten Sonderzeichen. Das gilt auch für Datenbankabfragen! Mit dem mysql-Support von PHP ist es zwar nicht möglich mehrere Abfragen in einen mysql_query-Aufruf zu packen, aber das könnte sich ja ändern. Ein vernünftig escapter String, der in dem mysql_query-Aufruf in Anführungszeichen eingeschlossen ist, sorgt dafür dass kein böswilliger Benutzer etwas wie ";DROP DATABASE bla in einer Variablen verwendet.

Ein anderer Punkt der gerne gegen die Sicherheit von PHP ins Feld geführt wird ist der, dass vom Benutzer übergebene Variablen ohne weiteres zutun des Progammierers global im PHP-Skript gesetzt werden. Dagegen hilft es einfach dafür zu sorgen, dass alle Variablen die intern verwendet werden, entweder garantiert vernünftig initialisiert werden oder am Anfang des Skriptes unset()ed werden.

Das Problem mit dem PHP- oder HTML-Code in Usereingaben ist relativ einfach zu lösen: Ich habe immer eine Funktion definiert die nichts anderes macht, als echo htmlentities(). In den Skripten werden alle Ausgaben von benutzerdefinierten Eingaben nur über diese Funktion ausgeführt. Ein böser Benutzer kann nun soviel PHP- und HTML-Code eingeben wie er will, er wird nur angezeigt werden und niemals ausgeführt. (eval() ist sowieso böse)

FARGE: In wieweit macht es Sinn das oben gesagte auch auf eventuell vorhandene eignen (Text-) Konfigurationsdateien anzuwenden? Man weis ja nie... Oder gilt eher, wenn die erst manipuliert wurden ist es es schon zu spät?

Wenn jemand Dateien auf dem Server verändern kann, kann er ganz andere Sachen machen. Sorge einfach dafür, dass dein Skript deine Konfigurationsdatei in jedem Falle einliest, so dass der Benutzer die dort angegebenen Variablen nicht überschreiben kann.

Punkt 2: 'Environment'-Variablen prüfen.

Kann ich nichts zu sagen.

Punkt 3: Dateien/Dateihandling.

* Dateien in die geschrieben werden soll in Unterverzeichnisse ablegen und nur in diesen Schreibzugriffe zulassen.
* Wenn übergebene Daten z.B. zur Generierung von HTML-Dateien benutzt werden darauf achten das keine 'aktiven Inhalte' wie eingebetetes PHP oder auch SSI darin vorkommen. (Bsp. PHP-Dateien, da dort möglicherweise ein "echo phpinfo();" drinnen stehen könnte, das Auskunft über die interne Struktur des Servers liefert)

s.o.
Wenn ich benutzerdefinierte Sachen anzeigen will, packe ich sie meist in eine Datenbank und so kann beim Auslesen schonmal kein PHP-Code ausgeführt werden. Als Schutz vor HTML muss htmlentities() reichen.

Punkt 4: Datei-Upload.

* Überprüfen ob korrektes Dateiformat. Z.B. über die MimeType-Angabe.
* Auf Dateigröße überprüfen. (Beschränkungen der Dateigröße ins Programm fest verdrahten und sich nicht auf 'Hidden-Formular-Fields' mit der gespeicherten Größe verlassen.)
* Alles unter Punkt 3 aufgeführtes.

FRAGE: Die MimeType-Angabe ist doch bestimmt auch nicht gerade die sicherste Methode das Dateiformat zu prüfen, oder? Die wird doch vom Browser mit übergeben, weshalb da im Prinzip doch beliebiges drin auftauchen könnte. Wäre also eine Überprüfung auf Dateiendungen nicht sinnvoller?

Die Dateigrößenüberprüfung musst du in jedem Fall durchführen, alles andere ist nicht zuverlässig. Du solltest dich aber beim Dateityp weder auf die MimeType-Angabe (wird vom Benutzer geliefert) noch auf die Dateiendung (wird auch vom Benutzer geliefert) verlassen. Eine gute Möglichkeit den Dateityp zu überprüfen ist file(1), das auf den meisten *n*x-Maschinen vorhanden sein sollte.

Um zu verhindern, dass der Benutzer den Dateiupload ganz weglässt und in einem Formularfeld den Namen einer Datei auf dem Server angibt, stellt PHP ja schon Funktionen wie is_uploaded_file() und move_uploaded_file() bereit.

Punkt 5: Sonstiges.

* Auf Buffer-Overflow achten.
FRAGE: In wieweit Betrifft das noch Scriptsprachen? In PHP z.B. kann man eh keine explizite Größe von Variablen festlegen und zweitens scheinen ihn übergroße Werte nicht zu sonderlich zu stören (Quick-Test von eben) sondern er begrenzt diese Werte automatisch.

FRAGE: Andere Frage, ähnliches Thema. Was ist, wenn ich den Script übergroße Mengen an Daten (z.B. per GET) zuschicke? Glaube irgendwo einmal gelesen zu haben das man damit die Programme wunderbar abschießen können soll. Wenn ja, was kann man dagegen machen? Ist das Script nicht schon gecrasht bevor ich überhaupt dazu gekommen bin eventuelle Prüfungen auf Buffer-Overflows durchzuführen?

Buffer Overflows in PHP selbst fallen wirklich eher in den Bereich der PHP-Entwickler, da dein Skript wahrscheinlich zu nichts kommen wird. Vorher ist der Webserver oder der PHP-Interpreter weg.

* Wichtige (Konfigurations-) Daten (z.B. Paßworte) immer verschlüsseln.

FRAGE: [Nur PHP?] Sollten wichtige (Konfigurations-) Daten nicht in 'ausführbare Dateien' abgelegt werden? Die Nachfrage bezieht sich auf ein (Forums-) Script (in PHP geschrieben) das ich vor einiger Zeit gefunden hatte. Dort hatte der Autor seine ganzen Konfigurationsdaten in einer *.inc Datei als normale Variablen in der Form "$var = wert;" deklariert. (So übrigens auch beim unverschlüsselten Paßwort) Nun soll es aber mit PHP laut Handbuch per 'Include' auch erlaubt sein Dateien auf fremden Servern in seine Scripte einzubinden. Würde das nicht bedeuten das ich mir in aller Seelenruhe diese Datei in mein eigenes Miniscript linken kann, um mir dann alle Werte ausgeben zu lassen? Oder habe ich da im PHP-Handbuch etwas falsch verstanden?

Warum man diese Dateien auf Teufel komm raus mit .inc enden lassen muss, ist mir auch nicht klar. Meine Konfigurationsdateien heissen config.php o.ä. Darin finden sich dann die Konfigurationsvariablen in einem PHP-Block. Es ist dann nicht mehr möglich diese Variablen auf irgendeine Weise von außerhalb auszulesen. (Es sei denn der Angreifer hat direkten Shellzugriff, und dann hasst du andere Probleme)
Das mit dem include über HTTP ist zwar im Prinzip richtig, aber auch hier ist es nicht möglich die Variablen zu bekommen, da der PHP-Interpreter vorher den PHP-Block rausgeschmissen hat. Das einzige Problem dass es hier gibt, ist wenn der PHP-Interpreter versagt. Da gab es mal einen Bug in der PHP/Apache-Kombination mit dem man die PHP-Ausführung von aussen unter bestimmten Umständen abschalten konnte. Achte also darauf, dass du immer eine aktuelle Version hast.

Was Passwörter angeht: Üblicherweise verwende packe ich einen MD5-Hash aus Benutzername und Passwort in die Datenbank. Damit dürfte es für einen Angreifer nicht - oder nur sehr schwer möglich - sein das Passwort irgendwie zu erfahren und zu benutzen. Da hierbei das Passwort im Klartext übermittelt werden muss, z.B. per HTTP-Auth, sollte man aber möglichst HTTPS verwenden.

--
Henryk Plötz
Grüße von der Ostsee