Schutz vor CSRF Angriffen
Wenn man mit der Webentwicklung anfängt, erfährt man relativ schnell, dass man auf die Sicherheit seiner Scripts achten soll. Eine klassische Lektion befasst sich mit SQL-Injections. Man lernt, wie man Anfragen filtert und gefahrlos weiterverarbeitet.
Ein anderes Thema wird allerdings nur selten erwähnt: CSRF (Cross-Site-Request-Forgery). Dabei geht es darum, einem (eingeloggten) Benutzer Anfragen unterzuschieben, die dann mit seinen Rechten ausgeführt werden. Ein Angreifer kann so z.B. das Passwort ändern oder einen neuen Benutzer anlegen.
Wie funktioniert CSRF?
Der Angreifer erstellt auf seiner eigenen Seite ein Formular, z.B. für eine Registrierung. Zusätzlich zu den sichtbaren Feldern gibt es aber auch noch versteckte mit Namen für ein anderes Formular. Außerdem ist als Ziel (action="…"
) die Seite angegeben, welche angegriffen werden soll.
Ein Benutzer schickt nun dieses Formular ab. Dabei wird die Anfrage aber an eine andere Seite geschickt, auf welcher der Benutzer eingeloggt ist. Für die angegriffene Seite sieht es aus, als wenn der Benutzer dort ein Formular abgeschickt hat, z.B. um das Passwort zu ändern. Anschließend kann sich der Angreifer mit dem neuen Passwort einloggen und hat somit Zugang zum System.
Man kann das obige Szenario sogar noch gefährlicher machen, indem man die Anfrage in einem versteckten Frame ausführt. Dadurch bekommt der Benutzer nichts von der Passwortänderung mit. Solange man dann nicht in den Quelltext schaut, ist so ein Angriff nur schwer zu erkennen (man könnte mit JavaScript dafür sorgen, dass das Formular auch an die Seite geschickt wird, wo sich der Benutzer anmelden wollte).
Weiterhin kann man CSRF-Angriffe auch mittels GET-Requests durchführen. Der Angreifer bindet dazu z.B. ein Bild auf seiner Seite ein, dessen src="…"
die jeweilige URL der angegriffenen Seite ist.
Wie kann man sich schützen?
Der erste Schritt ist, dass man GET nur für Aktionen ohne Seiteneffekte nutzt. Dies ist in RFC 2616 auch so beschrieben.
Bei Formularen (welche als POST gesendet werden) ist es üblich, ein Token in einem versteckten Feld mitzusenden. Dem Angreifer fehlt dieses (für jeden Benutzer oder jedes Formular individuell erstellte) Token, sodass die gefälschten Daten leicht erkannt werden können. In diesem Zusammenhang ist es auch wichtig, auf XSS-Angriffe zu achten, weil dadurch leicht das Token ausgelesen werden kann.
Man kann sessionbasierte und requestbasierte Token unterscheiden. Während ein Sessiontoken für die gesamte Session gültig ist, hat beim requestbasierten Token jedes Formular ein neues. Möchte man auch GET-Requests absichern, empfiehlt sich ein requestbasiertes Token, da ansonsten die Gefahr besteht, dass ein Dritter an die URL gelangt (z.B. beim Kopieren des Links in ein Forum).
AntiCSRF
Für php habe ich eine einfach zu verwendente Klasse geschrieben, welche grundlegenden Schutz gegen CSRF-Angriffe bietet. Es wird für jedes Formular ein neues Token erzeugt (requestbasiert), welches nur ein einziges Mal überprüft werden kann und ansonsten bis zum Ende der Session gültig ist. Außerdem stehen Helper für Formulare und URLs zur Verfügung. AntiCSRF gibt es auf GitHub unter der GPLv3.