-
Notifications
You must be signed in to change notification settings - Fork 4
Session Hijacking
Das Thema Session-Hijacking ist mittlerweile so alt wie die Möglichkeit, sich auf Webseiten einzuloggen. Aber was versteht man unter Session-Hijacking genau und wie kann man sich dagegen schützen? Vorab sei dazu gesagt, das man es niemals vollkommen unterbinden kann. Man kann es nur so weit erschweren, das der zeitliche sowie arbeitstechnische Aufwand das Hijacking uninteressant macht.
Ein gültiger Login auf einer Website wird als Session bezeichnet. Das Wort Hijack/Hijacking bedeutet Entführung. Daraus lässt sich schlussfolgern, das Session-Hijacking (übersetzt Sitzungs-Entführung) bedeutet, das eine Sitzung/Login eines anderen Benutzers entführt wird. Dies wird dadurch verursacht, das oftmals für eine Session nur ein einziger statischer Schlüssel verwendet wird.
Browserplugins oder speziell entwickelte Browser geben einem die Möglichkeit, Headerinformationen zu manipulieren. Das HTTP-Protokoll und dessen Umsetzung spielt dabei eine gewichtige Rolle und das Verständnis darüber ist absolut wichtig, um die Angriffsflächen zu verstehen. HTTP arbeitet bei einem Request (Anfrage) sowie bei einer Response (Rückmeldung/Antwort) immer nach dem gleichen Schema. Jede Anfrage sowie jede Response besteht entweder aus einem oder zwei Teilen. Dem Header (Kopf) und dem Body (Körper). Jeder Request und jede Response hat grundsätzlich einen Header. Der Körper wird nur verwendet, wenn Informationen an den Server oder an den Client gesendet werden.
Der Aufbau dieser zwei Teile folgen Standards, die es erleichtern, Software zu schreiben, die diese Informationen verstehen. Den Kopf einer Anfrage/Response definieren die Art und Weise, wie die Gegenseite mit dem Körper verfahren werden soll und geben zusätzlich an, ob eine Anfrage korrekt war oder überhaupt bedient werden kann. All diese Informationen liegen unverschlüsselt in einer Zeichenkette vor.
Die Session-Informationen befinden sich meistens im Kopf einer Anfrage bei den sogenannten Cookies. Selten wird auch die Session-ID über die URL übertragen. Letzeres sollte man allerdings nach Möglichkeit nicht tun. Dafür gibt es klare Gründe. Zum einen kann eine URL nur eine begrenzte Größe haben, was die Möglichkeit an Schutzmaßnahmen einschränken kann und zum anderen lässt sich die ID auch vom Bildschirm im Klartext ablesen, was bei Social-Engineering Attacken passieren kann (Internetkaffee, Brille mit Kamera und eine kleine Ablenkung und schon setzt sich der Angreifer zuhause hin und loggt sich ein, ohne das man was gemerkt hat). Ebenfalls ist das kopieren von Links problematisch, da man durchaus dazu verleitet wird, einen Link zusammen mit der Session-ID zu kopieren. Damit kann man einem Angreifer sein Passwort auch direkt geben.
Die fiesesten Arten von Angriffen basieren auf zusätzliche Sicherheitslücken an anderen Stellen. Die beiden wichtigsten Sicherheitslücken sind eine unverschlüsselte Verbindung, die es erlauben würde, Paket-Sniffern die Daten im Klartext zu entfangen. Die zweite Sicherheitslücke wäre das Cross-Site-Scripting. Gerade Anwendungen mit einem Message-System sind für solche Lücken einfach zu handhaben, da die erfassten Infos per Ajax als getarntes Nachrichten-Formular asynchron versendet werden, ohne das der Nutzer dies merkt. Der Angreifer hat dann die Session-ID dierekt ins einem Postfach und der Absender ist die Person, dem die ID gehört.
Ebenfalls ist es möglich, ein Javscript von einem externen Server zu laden. Dadurch wird Origin-Domain umgangen und das fremde Script kann auf die Informationen der Session zugreifen. Dadurch muss der CSS-Hack sich nur auf wenig Code beschränken und der restliche Code kann ohne Probleme sein Werk tun. Es muss nur eine Möglichkeit geben, die Informationen auch an ein passendes Ziel zu hinterlegen. Ein Message-System ist hier ebenfalls sehr einfach zu nutzen.
Session-ID's können aber auch auf viel banalere Weise auf einen fremden Server gelangen. Ein CSS bindet eine Grafik in HTML ein und die URL besitzt die Session-ID. Denn man kann auch ein Script ansprechen, um eine Ressource wie ein Bild zu bekommen:
http://coolepics.de/index.php?bild=gehackt&session=id_asdh12312k312b3djsjasbd
Hier kann sich mit dem Schlüssel bild der Angreifer eines Aussuchen, mit dem er sein Opfer verhöhnen kann. Aber ein Javscript hat dem Bild noch als zusätzlichen Parameter die Session-ID übergeben. Das Script (index.php) auf dem fremden Server muss nur ein Bild zurückliefern und den Schlüssel in einer Datenbank speichern. Dort kann er nachsehen und sich dann einloggen.
Kommen wir nun zu möglichen Gegenmaßnahmen. Wie wir bereits festgestellt haben, ist das erfassen von fremden Sessioninformationen vielseitig möglich. Nun müssen wir das ganze eben nur noch erschweren.
Verwendet nach Möglichkeit immer verschlüsselte Verbindungen. Nahezu die meisten Webhostinganbieter unterstützen HTTPS und ein Zertifikat ist dank Projekte wie "Let's Encrypt" auch für Lau zu haben. Verschlüsseln ist also auch kein Kostenfaktor mehr. Und es hilft ungemein, das Mitschneiden von Datenpaketen zu erschweren. Denn der Angreifer bekommt dadurch nur noch Datensalat, den er mit seinen mitteln garnicht erst versuchen brauch, zu entschlüsseln.
Ebenfalls sollte man immer Social-Engineering im Hinterkopf haben. Es gibt Leute, die viel in kürzester Zeit erfassen und merken können. Nehmt also Session-Schlüssel, die nach Möglichkeit eine moderate Länge haben. Im Notfall kodiert es noch mit Base64. Dadurch wird ein 64-stelliger Hash um bis zu 35% länger und es muss sich ein viel größerer Zeichenbereich gemerkt werden. Aber noch besser ist es, die Session überhaupt nicht lesbar auf dem Monitor zu bringen. Verwendet einfach Cookies und weder Nutzer noch Angreifer kommt an diesen ran, ohne sich die Cookies in den Browsereinstellungen anzeigen zu lassen. Und das fällt auf!
Diese Lücke lässt sich damit aushebeln, das alle Benutzereingaben nur verarbeitet wieder ausgegeben werden. Benutzernamen sollten nach Möglichkeiteine gewisse Länge nicht überschreiben. In z.B. 96 Zeichen bekommt man weniger Javascript oder HTML unter als in 256 Zeichen (kompletter Datenbank-Varchar). Schränkt dies auch in der Datenbank bereits ein. Das gleiche gilt für Kurzdaten wie zum Beispiel Forenthementitel. Da reichen 128 Zeichen eigentlich aus. Bei Begrenzten Feldgrößen filtert verbotene Zeichen lieber aus, anstatt sie mit z.B. SpecialChars umzuwandeln. Denn aus einem verbotenen Zeichen werden 4-5 erlaubte.
Größere Texte sollten mit SpecialChars behandelt werden (und zwar inklusive dem Singlequote). Nutzt zur Formatierung eine abstrahierte Auszeichnungssprache wie BBCode oder Markdown. Die wichtigsten WYSIWYG-Editoren unterstützen auch solche Markups mit Hilfe von Plugins. Und wenn HTML doch erlaubt sein soll, dann nutzt einen Allowed-Filter. HTML ist ein ziemlich beständiges Format. Es kommt eher vor, das Neuerungen nicht gewünscht sind als das welche kommen, die gewünscht sind.
Kleine Anmerkung: Ein solcher Filter ist auch für PBExpress als Modul geplant. Dieser kodiert zuerst den Inhalt mit SpecialChars und filtern hinterher die erlaubten Konstrukte heraus und demaskiert diese wieder.
Und nicht vergessen das setzen des Cookie-Attributs HTTP-Only. Wenn ihr keine speziellen Javascript-Routinen einsetzt, die auf die Cookies zugreifen müssen, sendet die Cookies bitte mit dem Attribut HTTP-Only an den Browser. Dadurch wird der Zugriff auf die Cookies durch Javascript Browserseitig unterbunden.
Sollte all dies nicht helfen und ein Schlüssel gelangt dennoch in Fremde Hände, dann hilft nur noch, etwas über die Schlüssel zu tun und die Wahrscheinlichkeit für eine Übernahme einzuschränken. Ein Schlüssel muss über die Session hinnaus natürlich statisch bleiben. Es macht keinen Sinn, für jeden Aufruf einen neuen Session-Schlüssel zu erzeugen, die alte Session zu löschen und eine neue anzulegen. Das bremst die Anwendung nur unnötig aus. Stattdessen sollte man einenzweiten dynamischen Schlüssel nutzen. Nach dem Login speichert man den UserAgent die User-ID, eine Zufallszahl und am besten den letzten Zeitpunkt des Requests in der Session. PBEFileSession bietet dafür eine Prozedur, die dies an Hand des Last-Modify-Datums der Datei erkennt. Hängt diese aneinander und hashed daraus mit MD5, SHA 1-3 oder was ihr nehmen wollt einen weiteren Schlüssel. Bei jedem Aufruf ermittelt ihr den UserAgent sowie den zweiten Schlüssel aus dem Cookie, hängt den UserAgent wieder an die anderen Informationen aus der Session und vergleicht dann die Schlüssel. Unterscheiden diese sich, wurde entweder vom Benutzer selbst bereits ein zwischenzeitlicher Request ausgelöst, wodurch der erfasste Schlüssel des Angreifers nicht mehr funktioniert. Oder er verwendet schlicht einen anderen Browser oder er versucht gerade, den zweiten Schlüssel zu Bruteforcen.
KLEINER TIPP: Wenn eure Daten auf der Seite und die Daten eurer Nutzer sensibel sind, dann schränkt den Login auch auf eine bestimmte Gültigkeitsdauer ein und nehmt die IP-Adresse ebenfalls in den dynamischen Schlüssel mit auf. Jedes Datenstück, das der Browser liefern kann und die Funktionalität nicht einschränken würde, kann dafür genutzt werden. Darf euer Login nur während der Browser-Sitzung gültig sein oder maximal 24 Stunden, dann nehmt auch die IP dazu.
Sessions sind im PBEFileSession-Modul sowie auch in PHP in Dateiform auf der Festplatte des Servers gespeichert. Wenn die Möglichkeit besteht, filtert die Dateien nach toten Sessions (Last-Modified-Date) und löscht diese nach gewisser Zeit. Mit PBExpress kann man dies mit einem Nebenläufigen Thread machen lassen. Für PHP empfehle ich, einen Cron-Job dafür anzulegen, wenn die Möglichkeit besteht. Wenn ihr aber nur ein Hostingpaket habt, besteht diese Möglichkeit in den allermeisten Fällen nicht. Dann empfiehlt es sich, die Session in serialisierten Arrays in die Datenbank zu verlagern und Regelmäßig ein "... DELETE .... WHERE last_modified<".time()+XXXXX auszuführen. Dieses Mittel hilft gegen Session-Bruteforce.