Christian Seiler: Challenge-Response-Authentifizierung

Beitrag lesen

Hallo Dennis,

Nein, bei einem korrekt implementierten Challenge-Response-Verfahren wirst Du nur bei "Live-Angriffen" (d.h. jemand schneidet die Session-ID mit und nutzt diese sofort und nicht erst ein paar Stunden später) den Zugang zum System kompromittieren

Davon rede ich doch *g*

Nein. ;-)

…solange du das nicht [SSL benutzt] haben alle Loginsystem mindestens eine Schwachstelle,
  nämlich genau da wo Benutzername und Passwort übermittelt werden, in welcher Form auch immer
  das konkret geschehen mag.

Wenn der Angreifer genau den Request mitschneidet, bei welchem Benutzername und Passwort übermittelt werden (den gibt es natürlich nur einmal, wenn der Benutzer sich einloggt), dann hat er alles, was er braucht um sich in Zukunft selber einloggen zu können. (Hierbei spielt es auch keine Rolle, ob man das Passwort im Klartext oder als MD5-Hash übermittelt.)

Falsch, das ist gerade der Witz an Challenge-Response.

Wenn Du es so willst, kann man auch sagen, dass Challenge-Response ein Zero-Knowledge-Beweis des Clients ist, dass er das Passwort kennt.

Im Prinzip funktioniert Challenge-Response so:

A. Server sendet Client eine Zufallszahl.
B. Client sendet Server sowas wie Hash(Passwort + Zufallszahl)
C. Server generiert selbst Hash(Passwort + Zufallszahl) und vergleicht das mit der Clientangabe.

Bei A. muss der Server natürlich jedes Mal eine neue Zufallszahl schicken und der Raum der Zufallszahlen muss groß genug sein, damit es funktioniert.

Außerdem muss das Passwort im Klartext beim Server vorliegen. Gut, das muss nicht zwangsläufig der Fall sein, wenn es gehasht vorliegt, kann der Client auch Hash(Hash(Passwort) + Zufallszahl) machen. Wenn es zudem noch gesaltet vorliegt auf dem Server, dann wird's aufwändiger. Denn dann muss der Client vom Server vorher den korrekten Salt für den spezifischen User erfragen. Nachdem der Server aber nicht einfach so hergeben soll ob ein User existiert oder nicht (zumindest in der Regel), muss der Server *immer* ein Salt an den Client zurückliefern (auch bei ungültigen Usernamen) - und dann auch noch immer das gleiche für den gleichen User. Muss man sich halt was dafür überlegen. ;-)

Im Web kann man das ganze mit JavaScript machen - der Server übergibt dem JavaScript die Zufallszahl, merkt sie sich aber intern noch in der eigenen Session (wenn er es nur wieder als Formularfeld übergibt, könnte das von einem Angreifer gefälscht werden -> Sicherheit dahin). JavaScript macht dann beim Abschicken des Formulares das ganze Mojo (erfragt u.U. vorher noch per AJAX den Salt zum Usernamen) und schickt dem Server nur noch das endgültige Hash-Gemix aus Passwort, Challenge-Zufallszahl und evtl. Salt. Der Server kann das dann verifizieren.

Ich hoffe, ich konnte hiermit mal etwas anreißen, warum Challenge-Response durchaus eine praktikable Möglichkeit ist, um Nicht-SSL-Verschlüsselte Verbindungen zumindest gegen bestimmte Angriffe abzuschotten. Wirkliche Sicherheit hat man natürlich nur mit SSL/TLS (ok, für eine Atombomben-Steuerung wäre mir vmtl. auch SSL/TLS etwas zu unsicher, aber für fast alles andere ist's ausreichend ;-)).

Beispiel: Ein Admin-Interface eines Weblogs. Da ist es relativ egal, ob jemand nun die Beiträge schon vorab lesen kann. Wenn er aber Zugriff darauf erlangt und dann seinen Unsinn treiben kann, wird's problematisch.

Ja, aber weil das Session-Cookie bei jedem Request vom Browser übermittelt werden muss, erhält der Angreifer auch in diesem Beispiel Vollzugriff auf das System, sofern er live agiert und nicht erst Stunden später handelt, sprich solange die Session-ID noch gültig ist (dies impliziert, dass der Benutzer sich noch nicht ausgeloggt hat).

Ja, deswegen schrieb ich ja, dass Challenge-Response gegen Live-Angriffe (!) nichts bewirkt [*]. Auch session_regenerate_id bei jedem Request ist keine Schutzmaßnahme, da - solange eine Seite angezeigt wird und der User nichts macht - die letzte bekannte Session-ID noch gütlig ist. In der Zeit kann ein Angreifer selbst einen weiteren Request absetzen und damit ist zum einen a) der User rausgeflogen (seine Session-ID ist ja nicht mehr gültig) und der Angreifer hat die Kontrolle. Bringt also absolut gar nichts.

Zudem wird session_regenerate_id() bei jedem Request den User IRGENDWANN zwangsläufig mal komplett rauswerfen - spätestens bei mehreren offenen Tabs. Das funktioniert nämlich bei jedem Request garantiert nicht zuverlässig, nervt also legitime Benutzer nur, ohne, dass es Sicherheit bringt.

session_regenerate_id() hat aber dennoch seine extrem wichtige und sinnvolle Anwendung bezüglich Session-Sicherheit: Es ist gut gegen Session-Fixation. Stell Dir vor ich schicke Dir einen Link zu einer Seite auf der Du registriert bist mit einer von mir vorher bereits erzeugten Session-ID, die ich kenne. Dann bist Du auf diese Session-ID "fixiert". Wenn Du Dich dann einloggst (URL stimmt, Script-Code habe ich auch nicht eingeschleust oder so), dann habe ich trotzdem Zugriff auf das was Du hast, weil ich ja die Session-ID, die Du verwendest, bereits kenne.

Daher: Bei einem erfolgreichen Login-Vorgang sollte man session_regenerate_id() durchführen, damit Session Fixation nicht möglich ist. Oder allgemeiner: Immer dann, wenn sich die Privilegien eines Users innerhalb einer Session erhöhen (!), sollte session_regenerate_id() ausgeführt werden. Damit beugt man Session Fixation vor. Für alles andere ist die Funktion unnütz und eher schädlich, da eine massive Anwendung nur zu Problemen bei legitimen Nutzern führt.

Viele Grüße,
Christian

[*] Auch das ist strenggenommen nicht ganz richtig, denn es gibt immer noch den Fall, in dem das Passwort selbst schützenswerter ist, als der konkrete Zugang zu dem Teilsystem. Das wäre zum Beispiel der Fall, wenn man das gleiche Passwort für Webmail und Shell-Account hat - Webmail ist da viel unwichtiger als das Passwort selbst, da man damit noch den Shell-Account kompromittieren kann.