Sammlung von Sicherheitshinweisen für PHP-Entwickler (bezogen auf pSys)

Begonnen von k00ni, 20. Februar 2008, 17:49:57

Vorheriges Thema - Nächstes Thema

k00ni

Ich würde hier gern mal einen Pool anlegen für allgemeine Sicherheitshinweise. Diese können gehen von Dateisystemsicherheit über Datenbanken und XSS. Was weiß ich. Da ich gerade selber einen kleinen Rework meines Modules starte, merke ich an vielen Stellen Nachholebedarf.
1. MySQL (allgemein Datenbanken)
1.1 Abfrage von Usertypen
Dinge wie das Löschen von Usern oder Anlegen von Administratoren sollten nur Administratoren machen dürfen. Allgemein Abfragen oder Operationen, die in vertraulichen Bereichen stattfinden oder Systemdinge verändern, dürfen nur von Administratoren oder ggf. Moderatoren gemacht werden.
Hier kann man einfach mit dem Usertyp arbeiten:
 


Bei dem Beispiel wird eine Exception geworfen, wenn ein Nicht-Administrator auf das System zugreift. Weiter im Code wird die Exception gefangen und eine entsprechende Meldung ausgegeben. Nach dieser Abfragen kann dann die Funktion tun, was sie zu tun hat.1.2 Abfrage der Parameter an das QueryBei mir hier gibt es für jede größere Abfrage eine eigene PHP-Funktion. Und wenn diese eine User-ID erwartet und einen String bekommt, dann läuft irgendwas falsch. Hier sollte man erst die jeweiligen Parameter in den gewünschten Typ umwandeln und dann nochmal auf \"Gültigkeit\" prüfen. Dabei kann man folgendermaßen vorgehen:
function foo_bar ($I_foo, $Bo_bar)


 
 
Die Funktion erwartet einen Integer- und Boolean-Wert. Mittels der http://de.php.net/manual/de/ref.ctype.php\" rel=\"external nofollow\">ctype_*-Funktionen kann man die Typen sauber prüfen und bei negativen Resultaten entsprechend reagieren.
 


Für Dinge wie Boolean wird leider nichts geboten, wo man auf eine http://de.php.net/manual-lookup.php?pattern=is_&src=\" rel=\"external nofollow\">is_*-Funktion zurückgreifen muss, nämlich http://de.php.net/manual/de/function.is-bool.php\" rel=\"external nofollow\">is_bool.


 
 
Nun kann $I_foo ein String sein, das Skript erkennt es sauber und könnte mit einer Fehlermeldung abbrechen. Man kommt damit eventuellen Angriffen zuvor.
Eine Umwandlung in einen Integer-Wert benötigt man nicht mehr, da dies bereits durch die Funktion abgefragt wird und bei true man sicher sein kann, dass man es mit einem Integer zu tun hat.
1.3 Säubern von Strings
Arbeitet man mit Strings, dann wird es wichtig, diese zu säubern. Zeichen wie Anführungsstriche, Gänsefüsschen, Istgleich-Zeichen etc. gehören nicht in einen String. Dies kann man maskieren lassen oder bei Entdeckung dieser (mittels http://de2.php.net/manual/de/reference.pcre.pattern.modifiers.php\" rel=\"external nofollow\">Regulärer Ausdrücke), bspw. das Weiterarbeiten verhindern und eine Fehlermeldung ausgeben.  
Zum Maskieren von Strings kann man http://de2.php.net/manual/de/function.mysql-real-escape-string.php\" rel=\"external nofollow\">mysql_real_escape_string nutzen oder andere. Ist http://de2.php.net/manual/de/function.get-magic-quotes-gpc.php\" rel=\"external nofollow\">magic_quotes_gpc aktiviert, werden automatisch von PHP aus die Sonderzeichen in Zeichenketten maskiert. Dies sollte man ggf. beachten um keine doppelte Maskierung vorzunehmen.
 
1.4 Niemals SQL-Code ausgeben
Egal was passiert, niemals SQL-Code ausgeben. Wenn man nicht wissen sollte, was passiert, dann einfach ein
 

try
{
}
catch (Exception $e)
{
}

 
 
um den ungewissen Bereich machen und eventuell auftretende Fehler wenigstens \"unterdrücken\". Normalerweise ist das eine Todsünde und darf nicht passieren. Jeder Code muss sauber laufen und man muss die Möglichkeit haben auf eventuelle Fehler zu reagieren.


2. Dateiarbeit
2.1 Dateipfade von \"außen\" schmecken nicht
Was der Bauer nicht kennt, frisst er nicht. So solltet ihr mit Dateioperationen verfahren. Übergebene Variablen (bspw. mittels GET) haben nichts in der einer http://de2.php.net/manual/de/function.fopen.php\" rel=\"external nofollow\">fopen-Funktion zu suchen. Oft denken die Hoster so weit und verbieten dass direkte Öffnen von Dateien, die nicht auf dem eignen Server liegen. (http://www.php.net/manual/de/ref.filesystem.php\" rel=\"external nofollow\">allow_url_fopen)
Für PHP wurden schon viele Angriffstellen in Dateisystem-Funktionen etc. gemeldet und es wird in diesem Zusammenhang immer geraten die Parameter zu prüfen. Auch wenn magic_quotes angeschaltet ist!
Wie auch schon bei der Datenbankproblematik, man prüft vorher dass was man verarbeiten will. Es ist unklug davon auszugehen, dass alles was von außen kommt, sauber und OK ist. Dem ist oft so, aber nicht immer. Deshalb kann Funktionen nutzen wie http://www.php.net/manual/de/function.is-file.php\" rel=\"external nofollow\">is_file.
 
2.2 Zugriffsrechte
Auch hier würde ich nur Administratoren oder Anhängern bestimmter Usergruppe den Zugriff erlauben. Der Rest wird gnadenlos geblockt. Es sollte beim übergeben von Daten von einer Seite zur anderen mit Session gearbeitet werden. Nimmt man GET läuft man Gefahr, zu viel Angriffsfläche zu zeigen. Auch gilt wieder, was der Bauer nicht kennt, frisst er nicht.


3. Sessions
3.1 Laufzeit der Session begrenzen
Nutzt man Session, so bietet es sich an, die Laufzeit auf eine halbe Stunde oder so zu setzen. Cookies am besten raus und falls keine Session übergeben wird, dann Schotten dicht. (Hier sind nicht die Leute aus Schottland gemeint /uploads/emoticons/icon_e_biggrin.gif.1a84f5257b36e14b36d04985314f877f.gif\" alt=\":-D\" />)


Habt ihr Anregungen, Kritik oder eigene Vorschläge, dann postet sie ruhig. Ich würde sie dann zentral in dieser Übersicht zusammenstellen.  /uploads/emoticons/icon_e_smile.gif.f7ec63a2b1c3d90a9415e40455642502.gif\" alt=\":-)\" />
[edit] 21.02.2008, 16:16 - \"1.2 Abfrage der Parameter an das Query\" geändert auf ctype [/edit]
 - Editiert von k00ni am 21.02.2008, 16:16 -

1.1 Abfrage von Usertypen[/quote]
Gute Idee. Bietet leider derzeit im PSys keinen echten Schutz.
1.2 Abfrage der Parameter an das Query[/quote]
Es ist zwar nobel, dass das Problem erkannt wurde, jedoch ist es prinzipiell eher schlecht, fehlerhafte Daten zu reparieren. Die is_*-Funktionen sind hierbei nur wenig hilfreich, ctype_* ist das Mittel der Wahl.
1.3 Säubern von Strings[/quote]
Wie geschrieben: Aufräumen ist prinzipiell nicht anzuraten. Werden nicht-erwünschte Werte erkannt, sollte die Verarbeitung entsprechend terminiert werden. Dabei sollten Daten immer gegen Whitlists und nicht gegen Blacklists validiert werden.
Wie auch schon bei der Datenbankproblematik, man prüft vorher dass was man verarbeiten will. Es ist unklug davon auszugehen, dass alles was von außen kommt, sauber und OK ist. Dem ist oft so, aber nicht immer. Deshalb kann Funktionen nutzen wie is_file.[/quote]
Race Conditions beachten.
2.2 Zugriffsrechte[/quote]
Again: Derzeit ist das Berechtigungssystem in PSys zwar funktional, jedoch nicht ausreichend, um gewisse Angriffe zu verhindern.
3.1 Laufzeit der Session begrenzen[/quote]
Cargo Cult. Die Laufzeit einer Session ist eigentlich ziemlich unwichtig. Es gibt jedoch eine gewisse Anzahl an Events, welche die  Erstellung einer neuen Session rechtfertigen.

k00ni

Nabend,
Bietet leider derzeit im PSys keinen echten Schutz.[/quote]
Abwarten, da kommt sicher noch was.  /uploads/emoticons/icon_e_surprised.gif.a8707b3f35a569cb4cfe563fc72ef78d.gif\" alt=\":-o\" />
Die is_*-Funktionen sind hierbei nur wenig hilfreich, ctype_* ist das Mittel der Wahl.[/quote]
In wie weit sind die is_*-Funktionen ungeeignet? Zu fehleranfällig, zu ungenau ?
Wie geschrieben: Aufräumen ist prinzipiell nicht anzuraten. Werden nicht-erwünschte Werte erkannt, sollte die Verarbeitung entsprechend terminiert werden. Dabei sollten Daten immer gegen Whitlists und nicht gegen Blacklists validiert werden.[/quote]
Wie soll man bei Datensätzen mit Anführungszeichen vorgehen, zum Beispiel wenn man eigenen HTML-Code speichert? Ich finde das Maskieren an manchen Stellen schon hilfreich. Nur sollte bei kritischen Abfragen wie zum Beispiel dem Einloggen schon abgebrochen werden, falls hässliche Zeichen auftauchen.
Race Conditions beachten.[/quote]
Gerne. Haste einen weiterführenden Informatione ( / Links)? Habe gerade nur http://de.wikipedia.org/wiki/Race_condition\" rel=\"external nofollow\">wikipedia gelesen.
Die Laufzeit einer Session ist eigentlich ziemlich unwichtig. [/quote]
Ich könnte mir schon Angriffsszenarien vorstellen. Löscht man eine gültige Session-ID nach der Benutzung nicht, so könnte jeder Angreifer, der an diese ran kommt, sich als anderer User ausgeben. Gut, die Möglichkeit dies zu \"klauen\" muss erstmal gegeben sein. Aber meine Bank hält Sessions auch nur 10 - 20 Minuten am Leben.

In wie weit sind die is_*-Funktionen ungeeignet? Zu fehleranfällig, zu ungenau ?[/quote]
Die is_*-Funktionen testen nur, ob der Typ einer Ressource den Test besteht. Sie überprüfen nicht den Inhalt.
Wie soll man bei Datensätzen mit Anführungszeichen vorgehen, zum Beispiel wenn man eigenen HTML-Code speichert?[/quote]
Man könnte den Code z.B. in ein völlig ungefährliches Format bringen, welches prinzipiell keine Schadzeichen enthalten, kann, bspw. URL-kodiert oder Base64-kodiert. Mit keiner der beiden Kodierungen lassen sich SQL Injections durchführen.
Nur sollte bei kritischen Abfragen wie zum Beispiel dem Einloggen schon abgebrochen werden, falls hässliche Zeichen auftauchen. [/quote]
HTML wäre in deinem Szenario ja erlaubt, also gäbe es keinen Grund zum Abbruch. Das Problem ist, dass sich Blacklists nicht pflegen lassen, da z.B. mit Unicode einige tausend Zeichen existieren, die nicht angegeben werden sollen (wenn nur ASCII oder Latin-9 erwünscht ist). Dazu kommen dann noch kombiniert diakritische Zeichen, von denen es noch weitaus mehr gibt. Deswegen definiert man eine Whitelist von Zeichen, die erlaubt sind und prüft gegen diese. Der Ansatz lautet dann: Nur wenn erlaubte Zeichen in der von mir gewünschten Kodierung auftauchen und die Daten zu dem User gehören, wird der Login durchgeführt, ansonsten abgebrochen.
Ach ja: Passwörter müssen natürlich immer mit einem Salt versehen werden und Loginversuche auf eine bestimmte Anzahl pro Zeiteinheit begrenzt werden.
Gerne. Haste einen weiterführenden Informatione ( / Links)? Habe gerade nur wikipedia gelesen.[/quote]
Da steht prinzipiell alles wichtige. Die Gefahr besteht übrigens auch bei Datenbankoperationen. Je höher die Anzahl der Operationen, umso höher die Gefahr von Race Conditions.
Ich könnte mir schon Angriffsszenarien vorstellen. Löscht man eine gültige Session-ID nach der Benutzung nicht, so könnte jeder Angreifer, der an diese ran kommt, sich als anderer User ausgeben.[/quote]
Ich schrieb ja, dass es bestimmte Events gibt, die eine Operation mit der Session rechtfertigen, ein Logout gehört dazu.
Aber eine Session einfach nach einer halben Stunde zu beenden ist etwas zu viel des Guten - man müsste sich dann jede halbe Stunde neu einloggen.

k00ni

Was gibt es für Szenarien, wo man \"leicht\" erkennen kann, dass Race Condition auftreten können?
Ich würde dies hier gern als Beispiel mit anführen.

Powie


- Zwei oder mehr Prozesse auf dem Webserver öffnen die gleiche Datei und schreiben beide in diese.
- Zwei oder mehr Prozesse nutzen eine Datenbankverbindung und ändern beide den gleichen Datensatz.
- Zwei oder mehr Prozesse nutzen eine Datenbankverbindung, jedoch schreibt ein Prozesse, während der andere liest.

k00ni

Zwei Moderatoren ändern gleichzeitig den gleichen Post.[/quote]
- Zwei oder mehr Prozesse auf dem Webserver öffnen die gleiche Datei und schreiben beide in diese.- Zwei oder mehr Prozesse nutzen eine Datenbankverbindung und ändern beide den gleichen Datensatz.
- Zwei oder mehr Prozesse nutzen eine Datenbankverbindung, jedoch schreibt ein Prozesse, während der andere liest.
[/quote]
Hmm. Was könnte man da bezogen auf Datenbanken tun? Also ein User bearbeitet seinen Post, ein Mod kommt und will das Gleiche tun. Denkbar wäre dass man speichert, wenn der Post bearbeitet wird. Nur das Prüfen, ob und wie lange dran gearbeitet wird könnte sich als schwierig erweisen. Oder man sperrt den Post dann für 5 Minuten. Nur was passiert danach? Andererseits könnte man einfach den Bearbeiten-Button ausblenden und der Mod kommt garnicht erst in die Versuchung da drauf zu klicken.
Sind diese Abfragen umfangreich oder lassen sie sich \"einfach\" realisieren?

Was könnte man da bezogen auf Datenbanken tun?[/quote]
Tabellen exklusiv sperren. Transaktionen verwenden.
Sind diese Abfragen umfangreich oder lassen sie sich \"einfach\" realisieren?[/quote]
MYISAM unterstützt keine Transaktionen. Es wäre ein Umstieg auf InnoDB (oder BDB) nötig, was beim Publikum von PSys unmöglich sein dürfte (wenn Du dich gerade gefragt hast, was BDB bedeutet, gehörst Du zu diesem Publikum).
LOCKs dagegen kann man auch bei MYISAM verwenden, sind jedoch nicht atomar.
Übrigens: Race Conditions bei der Dateiverarbeitung sind gefährlicher. Denn hier kann man u.U. Prozesse mit root-Rechten ausführen, gerade wenn bei der Einrichtung des httpd und der Rechtezuteilung geschlampt wurde.
Eine Stelle, die ich schon lange kritisiere, ist die Verarbeitung von User-Avataren. Dort wird geprüft, ob die Dateirechte des User-Avatar-Verzeichnisses auf 0777 gesetzt sind. Bitte, was soll das? Warum bitte muss das Verzeichnis für jeden les- und beschreibbar sein? Auf Shared-Hosting-Systemen kann man so bequem Executables in dem Verzeichnis parken - schreibrechte hat ja eh jeder. Wenn ich im Source-Directory von PSys nach 777 greppe, finde ich ca. 20 Stellen, an denen solche Dateirechte geprüft oder gesetzt werden.

k00ni

Dort wird geprüft, ob die Dateirechte des User-Avatar-Verzeichnisses auf 0777 gesetzt sind. Bitte, was soll das?[/quote]
Welche Rechte würden denn ausreichen? Was ich mich gerade frage: Welcher \"User\" führt eigentlich die Webseite aus? Ist dass eine Art Gast oder ein bestimmter User (vom Typ her)? Je nachdem müsste man auch die Rechte setzen.
Ich hatte vor einiger Zeit mal die Idee gehabt, eventuell benötigte Rechte erst dann zu \"setzen\", wenn man sie braucht. Also möchte ein User einen Avatar hochladen und dies wird auch vom Admin gewünscht, dann könnte dieser ja vorläufig die Rechte dafür bekommen. Obs nun 777 sein muss, sei erstmal dahin gestellt. Ist er fertig mit dem Upload werden die Rechte wieder zurückgesetzt und fertig.
Wie könnte man bei dem Dateirechte-Problem am besten ansetzen?
 
Grüße

Welche Rechte würden denn ausreichen? Was ich mich gerade frage: Welcher \"User\" führt eigentlich die Webseite aus? Ist dass eine Art Gast oder ein bestimmter User (vom Typ her)? Je nachdem müsste man auch die Rechte setzen.[/quote]
Die Operationen werden meist(!) vom Besitzer des Webserver-Prozesses ausgeführt (oder in suexec-Umgebungen vom User, dem die Dateien sowieso gehören). Es würde also völlig ausreichen, etwa 0755 zu vergeben - der Besitzer darf schreiben, lesen und betreten. Gruppe und Welt nur Betreten und lesen.
Ich hatte vor einiger Zeit mal die Idee gehabt, eventuell benötigte Rechte erst dann zu \"setzen\", wenn man sie braucht. Also möchte ein User einen Avatar hochladen und dies wird auch vom Admin gewünscht, dann könnte dieser ja vorläufig die Rechte dafür bekommen. Obs nun 777 sein muss, sei erstmal dahin gestellt. Ist er fertig mit dem Upload werden die Rechte wieder zurückgesetzt und fertig.[/quote]
Das ist eine Race Condition, denn es werden niemals alle Operationen in einem Zyklus des Schedulers ausgeführt. Zumal es gar nicht notwendig ist, ständig Rechte neu zu setzen (kostet Performance), wenn man einmal ausreichende Rechte setzt.
Wie könnte man bei dem Dateirechte-Problem am besten ansetzen?[/quote]
Alle Skripte so ändern, dass man zumindest in der Vanilla-Version keinen Lücken aufmacht. Wenn ein User selbst zu großzügig Rechte verteilt ist das nur sein Problem. Wenn aber Software solche Einstellungen schon mitbringt, ist es fahrlässig.

all your base are belong to us / Discord