PHP - Häufige Sicherheitslücken

  • Obwohl auf meinen Thread keine Antwort kam, denke ich, dass doch trotzdem (vor allem bei Anfängern) Bedarf besteht. Aus diesem Grund hab ich diesen Beitrag vorbereitet.

    Wenn es um die Sicherung von Webseiten geht, sollte man grundsätzlich von einer Annahme ausgehen:

    Der Benutzer ist böse. Wo er Daten eingeben kann, wird er versuchen, eine Lücke zu entdecken.

    Man sollte sich niemals darauf verlassen, dass der Benutzer nicht weiß, wie man Sicherheitslücken ausnutzt, deswegen lässt man diese am besten gar nicht entstehen. Hier will ich ein paar der häufigsten Probleme beleuchten.

    Inhaltsverzeichnis

    • register_globals/import_request_variables()
    • XSS/CSRF
    • SQL Injection
    • Remote/Local File Inclusion
    • eval(), Systembefehle, SSI (Server Side Includes)
    • Ungehashte Passwörter
    • Allgemeines


    1. register_globals/import_request_variables()
    Die Server-Einstellung register_globals erlaubt es, in Formularen oder per URL übergebene Werte direkt im Script zu verwenden. Ein Wert, der z.B. in der URL http://example.com/index.php?id=4 id heißt, wäre im Script direkt als $id verfügbar.
    Was passiert nun aber, wenn wir folgendes Script admin.php haben?

    und ein Angreifer die URL http://example.com/admin.php?admin=1 aufruft? Dann wird der Wert von admin in $admin reingeschrieben und der Benutzer hat freien Zugriff auf die Adminoberfläche. Man sollte deswegen register_globals auf dem Server deaktivieren. Die Zeile

    PHP
    <?php ini_set("register_globals", "0"); ?>


    sollte in jedes Script ganz oben eingesetzt werden, wenn register_globals an ist. Dies kann mit phpinfo() überprüft werden.

    Ein ähnliches Verhalten wie register_globals ermöglicht die Funktion import_request_variables(). Aus den gleichen Gründen wie register_globals sollte man sie nicht verwenden.

    2. XSS/CSRF
    Die Idee beim Cross-Site Scripting (XSS) ist, den Benutzer bösartigen Javascript-Code ausführen zu lassen, der im schlimmsten Fall seine Cookies klaut. Ich weiß nicht, inwiefern ich hier das genaue Vorgehen erklären darf, deshalb lass ich es lieber.
    Der Angreifer will ein <script> Tag einbringen, das auf Userseite ausgeführt wird. Das trivialste Beispiel wäre folgendes Fehlerscript error.php:

    PHP
    <?php
    $message = $_GET['message'];
    echo "Es gab einen Fehler! Der Fehler war: ". $message;
    ?>


    Der Programmierer hat ein anderes Script erstellt,
    das im Fehlerfalle auf error.php?message=Fehlende%20Rechte umleitet. error.php gibt erwartungsgemäß

    Zitat

    Fehlende Rechte


    aus. Was ist nun, wenn ein Angreifer einen Link zu
    error.php?message=<script>alert('xss')</script> vorbereitet und an seine Opfer verschickt? Dann ist die Ausgabe

    Zitat

    <script>alert('xss')</script>


    Das Javascript wird auf der Seite des Users ausgeführt.
    CSRF (Cross-Site Request Forgery) kann unter Umständen mit XSS kombiniert sein. Das Ziel ist zum Beispiel, einen Admin eine bestimmte Aktion ausführen zu lassen, indem dieser einen Link anklickt.

    3. SQL Injection

    The User hat in seinem Lexikon-Eintrag schon ziemlich alles, was man als Entwickler wissen sollte, aufgeführt.
    Es wäre noch zu erwähnen, dass eine SQL-Injektion auch möglich ist, wenn man vom Benutzer eine Eingabe für ORDER BY erwartet, wie in diesem Beispiel:

    PHP
    //topliste ausgeben
    $sql="SELECT username, level, ruhm, gold FROM usertable ORDER BY $order DESC LIMIT 50"


    Man sollte deshalb prüfen, ob $order in der Liste der auszuwählenden Felder ist.

    Zusätzliche Informationen würden nur darüber gehen, wie man SQL Injections ausführt, und das gehört hier nicht hin ;)

    4. Remote/Local File Inclusion
    Die Kurzfassung ist: Man sollte keine Dateien inkludieren oder ihre Inhalte ausgeben, wenn der Benutzer einen Teil des Pfads selber übergeben kann. So wäre es einem Angreifer zum Beispiel möglich, Dateien, die auf seinem Webspace liegen, einzubinden, oder Inhalte von Dateien, die auf dem Server liegen (.htaccess und ähnliches) auszulesen.

    5. Man sollte fast absolut nie Benutzereingaben in solche Befehle wie eval(), system() oder sonstige einfügen, da dies immer ein Sicherheitsrisiko darstellt. Auch Server Side Includes, wenn man denn welche verwendet, sollten keine Benutzereingaben direkt verwenden.

    6. Siehe: Hashing.

    7. Allgemeines
    Zu beachten ist, dass diese möglichen Sicherheitslücken untereinander kombiniert werden könnten. So könnte zum Beispiel ein Angreifer in mySQL zuerst einen Fehler erzeugen und wenn ihm dann seine fehlerhafte Query ausgegeben wird, XSS einschmuggeln.
    Außerdem gelten die Hinweise hier sowohl für Daten, die über GET, also über die URL übergeben werden, als auch über POST. Ein Angriff über letzteres ist zwar meist kompliziert, aber definitiv nicht unmöglich.
    Manche neueren Webseiten verwenden URLs wie http://example.com/users/Afrael. Intern wird dabei das Script http://example.com/user.php?username=Afrael aufgerufen. Auch hier könnte man die beschriebenen Methoden anwenden, die URLs stellen also keine Sicherheitsmaßnahme, sondern allenfalls eine Bequemlichkeit für den User dar.
    Zuletzt ist noch von der Ausgabe von $_SERVER['PHP_SELF'] im Script abzuraten. Wird die URL nämlich manipuliert, ist XSS möglich.

    Anmerkungen, Ergänzungen usw bitte hier rein.

    Edit 1: Eine weitere Möglichkeit der Validierung, die ich vergessen habe zu erwähnen, sind Reguläre Ausdrücke. Wer sich darüber informieren will, kann im Internet nach RegExp oder regular expressions suchen. Zu beachten ist jedoch, dass diese meistens relativ langsam ausgeführt werden.

    Information will frei verfügbar sein.

    Don't eat unpeeled hedgehogs.

    3 Mal editiert, zuletzt von The User (16. Mai 2009 um 23:34) aus folgendem Grund: Edit 3: Hashing hinzugefügt

  • Zu 1:
    Einfacher ist es, die Funktion in der .htaccess zu deaktivieren:

    Code
    php_flag register_globals off

    Zu 2:
    Cookies können sehr wichtig sein, z.B. die Session-ID. Wenn ein anderer Server über XSS daran kommt, kann jemand, der diese Daten erhält, sich bei der Seite unter dem Namen des anderen einloggen.
    Vorsichtshalber:
    -Eine Variable $_SESSION['ip'] setzen und überprüfen, ob nicht eine andere IP zugreift. Wenn jemand wirklich von zwei Rechnern zugreifen will, kann man ja eine kleine Checkbox einbauen, um die Überprüfung zu deaktivieren. (so wird es auf Heise gemacht)
    -Bei wichtigen Operationen (z.B. Änderung des Passwortes/der Email-Adresse oder auch dem Löschen von Daten (wenn es eher selten vorkommt oder großflächig ist)) lieber noch einmal nach dem Passwort fragen.

    Zu 4:
    Es gibt auch zwei ganz einfache Vorkehrungen, die helfen:
    -.. herauswerfen oder auflösen. Bei '../../../../etc/passwd' fällt beispielsweise auf, dass das Verzeichnis komplett verlassen werden soll. Dann einfach einen Standard-Wert nehmen oder 404 ausgeben.
    -Mit file_exists überprüfen und somit dem Besucher Fehlermeldungen ersparen.

    Zu 5:
    Bei diesen Befehlen gilt eigentlich das selbe wie bei SQL-Injection, es liegt ja auch eine Code-Injection vor: Entweder die Nutzerdaten escapen, z.B. bei Kommandozeilen-Parametern. Dabei müssen " ", ' ' und insbesondere auch ` ` escaped werden. Außerdem sind auch andere Übertragungs-Möglichkeiten vorhanden:
    -Standard-Eingabe (mit popen oder proc_open)
    -Datei als Zwischenspeicher
    -Shared Memory
    Die normalen Anwendungsgebiete von eval lassen sich auch durch normalen PHP-Code abdecken, z. B. statt eval('$x = new ' . $classname . '(123);') geht auch $x = new $classname(123). Komplexere eval-Gebilde sind meist sehr langsam, da sie jedes Mal neu geparsed werden müssen und Optimierungen kaum möglich sind. eval sollte also praktisch nie verwendet werden.

    Viele liebe Grüße
    The User

  • Zitat

    Zusätzliche Informationen würden nur darüber gehen, wie man SQL Injections auführt, und das gehört hier nicht hin ;)


    Warum nicht?
    Das würde vielleicht zum Verständnis der Materie beitragen.

  • Weil ich nicht weiß, wie ausführlich ich das hier beschreiben darf. Das meiste, was ich meinte, kann nur passieren, wenn man Zahlen per GET empfängt, nicht sicherstellt, dass es Zahlen sind, und sie ohne Anführungsstriche in die Query einbaut.
    SELECT * FROM table WHERE id=$_GET['id'] zum Beispiel. Und das würde dann irgendwann auf ein SQL-Injection Tutorial hinauslaufen.
    Hätte gerne die Meinung eines Mods, bevor ich mich auf sowas einlasse :/

    Information will frei verfügbar sein.

    Don't eat unpeeled hedgehogs.

  • PHP
    $query = "SELECT story_id,story_title,story_desc,story_cat,story_digs,story_url,story_comments,user_name,user_id,story_thumb,story_time,story_tags FROM ".$config['db']['pre']."stories ORDER BY story_rating ASC LIMIT ".validate_input(($_GET['page']-1)*10).",10";
    }



    Ist dies hier gemeint? Steht in meiner index.php und auch anderswo.

    Einmal editiert, zuletzt von jojo87 (15. Mai 2009 um 18:46)

  • Das chmod 0777 ist bei einer vernünftigen Server-Konfiguration nicht falsch.
    Wenn nicht 0777 ausgewählt ist, gibt es oft Probleme, wenn manche Dateien per FTP, manche per PHP angelegt werden, da der PHP-Prozess eine uid unabhängig von der des Scripts besitzt.
    Wichtiger ist, dass auf dem Server open_basedir sinnvoll konfiguriert ist.

  • Am sinnvollsten ist es nicht 777 zu nehmen sondern nur da wo es auch wirklich benötigt wird, trägt also zu minder weniger sicherheit bei


    mfg