0251 / 590 837 15
info@a-coding-project.de

7 Möglichkeiten um deinen PHP Code zu hacken

Wer eine Website ins Netz stellt, sollte sich auch immer Gedanken um die Sicherheit machen. Man hört immer wieder von großen Datendiebstählen und anderen schlimmen Dingen. Wenn es einen selbst trifft, ist die Laune am Boden.

Doch, um einen sicheren PHP Code zu schreiben sollte man die Fallstricke kennen und vermeiden. Aus diesem Grund möchte ich euch heute mal eine kleine Sammlung bekannterer oder auch weniger bekannter Hacks vorstellen, die ihr am besten auf eurer Seite mal ausprobiert.
ACHTUNG: Wer solche Tricks ohne Erlaubnis des Eigentümers auf fremden Websites anwendet, riskiert rechtliche Folgen!

Ich werde erst einmal die klassischen Tricks vorstellen und später zu den vielleicht nicht ganz so Bekannten kommen.

1. SQL Injection

Mit das schlimmste, was euch passieren kann, ist wenn jemand Zugriff auf eure Datenbank bekommt. Dies kann zum Beispiel durch eine SQL Injection passieren. Dabei wird eine reguläre Abfrage durch manipulierte Eingaben so abgeändert, dass man beliebige SQL-Statements ausführen kann.

Beispiel:

<?PHP
    mysql_query("SELECT * "
               ."FROM users "
               ."WHERE nickname = '".$_GET['nickname']."'");
?>

Hier wird einfach mit MySQL nach einem Benutzernamen gesucht. Bei ganz normalen Nicknames wird das auch klappen. Nun kann man aber zum Beispiel folgendes aufrufen:

?nickname=’+or+1+=+1

Dann wird aus dem SQL-Statement folgendes:

SELECT *
FROM users
WHERE nickname = ''
or 1 = 1

Und schon hat der Angreifer Zugriff auf alle Benutzerdaten. Um dies zu vermeiden, muss man die Eingabe escapen, also die Sonderzeichen so umschreiben, dass sie richtig in die Datenbank geschrieben werden können. Dies geht in dem Beispiel über mysql_real_escape_string:

<?PHP
    mysql_query("SELECT * "
               ."FROM users "
               ."WHERE nickname = '".mysql_real_escape_string($_GET['nickname'])."'");
?>

Wenn man ein anderes Datenbanksystem benutzt, sollte man auch dessen escape-Funktion nehmen. Ich empfehle das Escapen immer direkt im Query zu machen. Egal, ob der Wert an anderer Stelle validiert wurde oder nicht. So ist man auf jeden Fall auf der sicheren Seite.

2. XSS – Cross Site Scripting

Es gibt viele Sachen, die auf eine Domain beschränkt sind. So kann man zum Beispiel keine Cookies von fremden Seiten auslesen. Denn das hätte fatale Folgen! Denn dadurch kann man sich zum Beispiel Session-IDs klauen und hat vollen Zugriff auf den jeweiligen Account.

Beim Cross-Site-Scripting geht es darum, Quellcode auf fremden Seiten zu platzieren und somit die Rechte der geschädigten Seite zu bekommen. Kleines Beispiel:

<?PHP
    echo "Hello".$_GET['nickname'];
?>

Hier kann man über den Get-Parameter nickname den Namen übergeben und wird gegrüßt. Nun ja, man kann aber auch Javascripts übergeben. Zum Beispiel: ?nickname=%3Cscript%3Ealert(‚XSS!‘)%3B%3C/script%3E .Wer nun die Seite besucht, wird mit einem Javascript-alert begrüßt. Online werden dort aber eher schlimmere Dinge als nur ein Alert stehen!

Der Ausweg sieht so aus:

<?PHP
    echo "Hello".htmlentities($_GET['nickname']);
?>

Nun bekommt man einfach ein script-Tag zusehen, es wird aber nicht ausgeführt. Auch das htmlentities sollte am besten direkt bei der HTML-Ausgabe stehen.

3. Verändern von Variablenwerten – register_globals sei Dank!

Wäre es nicht cool, einfach mal die Werte von Variablen zu ändern? Also das $admin auf true setzen, und schon hat man alle Rechte? Kein Problem, ist register_globals angeschaltet, kann man das ganz einfach machen:

<?PHP
    if($user->is_admin()){
        $admin=true;
    }
    if($admin){
        echo"Ich bin hier Chef!";
    }
?>

Ist register_globals eingeschaltet, kann man jetzt einfach „?admin=true“ an die Url hängen und wird zum Admin. Gott sei Dank, ist register_globals mittlerweile fast überall deaktiviert und auch bei PHP als DEPRECATED markiert.

Dennoch sollte man Variablen vor der Verwendung initialisieren. Das geht zum Beispiel so:

<?PHP
    $admin=false;
    if($user->is_admin()){
        $admin=true;
    }
    if($admin){
        echo"Ich bin hier Chef!";
    }
?>

Jetzt ist egal, was vorher im $admin steht, er wird vorher auf false gesetzt. Außerdem sollte man (auch der übersichtshalber) auf globale Variablen verzichten und objektorientiert arbeiten. So gelten die Variablen so oder so nur in ihrer Funktion.

4. Session Hijacking

Grade in der letzten Zeit ist hiervon vermehrt die Rede. Ähnlich wie eben beim Cross-Site-Scripting, kann man hiermit Sessions klauen. Sessions braucht man zum Beispiel um Authentifizierung zu ermöglichen. Es wird ein Cookie gesetzt, in dem eine eindeutige ID ist. Diese wird bei jeder Anfrage übermittelt und an der können wir den Benutzer wieder erkennen.

Das Problem dabei: Kommt jemand fremdes an diese ID (z.B. über schlecht gesichertes WLAN), kann er sich einfach die SESSION des ausspionierten Users schnappen.

Doch wie verhindert man das? Schließlich ist es eher ein Problem des Anwenders, oder? Aber genau dieser wird sich bei dir melden, wenn plötzlich sein Account nicht mehr ihm gehört.

Am besten umgeht man das Problem, in dem man HTTPS benutzt. Hierbei handelt es sich um eine verschlüsselte Verbindung, bei der man die Session-ID nicht mehr auslesen kann. HTTPS ist nur leider mit weiteren Kosten verbunden.

Wem das zu teuer ist, der kann einfach überprüfen, ob sich die IP oder auch der Browser geändert haben. Somit ist man zumindest etwas sicherer.

5. relative Pfade

Der include-Befehl wird oft genutzt um Unterseiten zu laden. Hierbei sollte man immer Prüfen, ob der Pfad auch ok ist. Übergibt man zum Beispiel ../ als GET-Parameter, so kann man einfach in ein übergeordnetes Verzeichnis springen!

6. Zahlen in Exponentialdarstellung

Ein häufig gemachter Fehler ist es, Zahlen nicht richtig zu überprüfen. Folgendes Beispiel:

<?PHP
    if(is_numeric($_GET['id']))
    {
        $sql="SELECT * "
              ."FROM user "
              ."WHERE id = ".$_GET['id'];
    }
?>

Jetzt sind nur Zahlen erlaubt, oder? Klar. Aber auch 2e5 ist eine Zahl. Nur in andere Schreibweise. Da diese Schreibweise von MySQL nicht unterstützt wird, kann man hier mit einem SQL-Fehler rechnen. In Falle einer ID sollte man hier mit is_integer arbeiten. Wer Kommazahlen benötigt, sollte prüfen, dass auch kein e in der Zahl vorkommt.

7. GET-Parameter als Array

Eine schöne Funktion von PHP ist es, dass man GET-Parameter auch als Array übergeben kann. So wird aus ?ids[]=1&ids[]=2 folgendes: array(1,2). Jetzt schaut euch mal folgenden Code an und überlegt, was das Problem sein könnte:

<?PHP
    echostrlen($_GET['name']);
?>

Gibt man als GET-Parameter nun ?name[]=test an, bekommt man folgende Warning:

Warning: strlen() expects parameter 1 to be string, array given in test.php on line 2

Aus diesem Grund, sollte man mit is_string vorher prüfen, ob es wirklich ein String ist. Auch ein Weglassen des Parameters würde in diesem Fall eine Warnung verursachen.

Allgemeine Tipps für mehr PHP-Sicherheit

In den Beispielen seht ihr, dass es einiges gibt, auf das man achten sollte. Aus diesem Grund möchte ich nochmal ein paar Allgemeine Tipps nennen, wie man viele Probleme umgehen kann.

  • Vertraue nichts, was außerhalb deiner Funktion liegt. Auch wenn es sich dabei um Daten aus eigenen Abfragen handelt. Es kann immer Probleme geben. Gerade alles was vom User kommt ist generell böse.
  • Escapes immer direkt an der Konvertierung-Stelle. Wie eben schon genannt, sollte man Escapes(z.B. htmlentities) immer an der Stelle verwenden, wo von einer Technologie in eine andere übergegangen wird (PHP => MySQL oder auch PHP => HTML). Klar kann man Sachen auch vorher escapen, nur man sollte sich nicht darauf verlassen, dass dies auch immer gemacht wird.
  • PHP-Fehler haben online nichts zu suchen! display_errors sollte auf jeden Fall aus sein. Denn durch sie bekommt der Angreifen Infos über den Quellcode, wenn auch nur zum kleinen Teil
  • .php vermeiden. Am besten ihr schreibt eure Urls mit mod_rewrite um, um die Technologie dahinter zu verschleiern. Klar PHP wird online das erste sein, was ein Angreifer vermutet, trotzdem kann es aber nicht schaden. Und: Sollte man mal zu ASP.net o. Ä. umziehen, kann man die Urls behalten.
  • Gebt so wenig Rechte wie möglich. Das gilt für PHP-Einstellungen, Schreibrechte und MySQL Rechte.
  • Lasst eure Sachen von anderen Leuten testen. Man selbst kann immer schnell was übersehen. Ihr könnt auch gerne mich fragen, es macht immer wieder Spaß 😉

Weitere Möglichkeiten?

Immer her damit 😉 Kann nie schaden darüber Bescheid zu wissen.

Kommentare

Oliver schrieb am 22.07.2012:

CSRF fehlt noch, sowie der Hinweis, Passwörter sicher zu speichern.

Tino Ruge schrieb am 22.07.2012:

Zum Thema "relative Pfade", die haben auc noch ein Fachwort, was sich "LFI" oder "Local File Inclusion" nennt. Davon gibt es auch eine "Abwandlung", das "RFI" -&gt; "Remote File Inclusion". Als Beispiel: index.php?page=http://meineseite.de/index Das zu "beheben" ist einfach, vorher mit "file_exists" prüfen, ob die Datei existiert. Das gibt bei PHP mit fremder URL immer false zurück.

Carsten schrieb am 22.07.2012:

Zum Thema "Datenbanksicherheit": Mit Sqlmap ( http://sqlmap.org/ ) kann man den eigenen DB-Setups recht eingehend auf den Zahn fühlen.

Harry schrieb am 29.07.2012:

Thema SQL-Injection: Ich empfehle wenn immer möglich Bind-Variablen. Dadurch wird seitens der Datenbank schon sehr viel abgefangen.

Herbrich schrieb am 10.08.2012:

Mich würde mal intrssieren ob es für den IIS6.0 und ASP.NET4.0 etwas geibt.

Al schrieb am 29.08.2012:

Hello Stefan, danke für die Inputs hier und die Mühe die du dir gemacht hast. Für mich sind die Tipps immer wieder wertvoll! Ich verfolge ma, hier weiter. Schönen Gruss, Al.

Raubritter schrieb am 06.11.2012:

es wäre noch sinnvoll auf clear_magic_quotes einzugehen.

Raubritter schrieb am 06.11.2012:

+ evtl etwas über: http://php.net/manual/de/book.pdo.php?

Christoph schrieb am 28.03.2013:

Statt Variablen für selects zu escapen sollte man binds benutzen. Die sind 100% sicher und haben den Vorteil, dass die Datenbank den select cachen kann.

Mark schrieb am 14.01.2014:

Das mit den Get-Parametern als Array, geht das auch bei POST-Parametern?

Stefan Wienströer schrieb am 15.01.2014:

jopp