Logik: Loginsystem - wie ist mein Vorschlag?

Moin!

Ich stehe wieder davor ein Loginsystem zu schreiben bzw. eine Loginklasse.

Sprache: PHP
DBMS: MySQL

wies aufgebaut werden soll:

Formular:
Name:
Passwort:

Beim abschicken: Javascript: Erstelle md5 Hashsumme rufe Dokument auf welches die Klasse "Loginsystem" aufruft

class Loginsystem{
function Login(Name, PW){
  // Nehme Name und PW entgegen
  // erstelle sha1 Hashsumme
  // Guck in Datenbank nach ob beides vorhanden ist
  // Lese Userid aus
  // Hashwert der Userid (sha1) = HU
  // Erstelle Session und schreibe dort HU rein
  // Erstelle Cookie und schreibe dort HU rein
  // Schreibe in die Datenbank aktuelles Datum und Uhrzeit rein + HU
  // Leite weiter
}

function auth(Cookie, Session){
  // vergleiche HU von Cookie und Session
     // Falls es nicht stimmt, abbrechen
  // Vergleiche dann mit der HU aus der Datenbank
     // Falls es nicht stimmt, abbrechen
  // Vergleiche das Datum und Uhrzeit,
     // Falls älter als 4 Std. , abbrechen
  // Setze Datum und Uhrzeit mit der aktuellen Uhrzeit (bei 4 std inaktivität ist man halt draußen)
  // Zeige Seite an, die Funktion auth.php wird in jeder Seite  aufgerufen

}

function Logout(){
  // Lösche Cookie
  // Lösche Session
  // Lösche Datum, Uhrzeit und HU
}

Was haltet ihr davon?
Was kann ich noch verbessern?

Liebe Grüße, die Logik

  1. Auf jeden Fall auf JavaScript verzichten.
    Schick die Daten per POST an deine .php und mach dort die Hashes.
    Wenn jemand JS abgedreht hat dann Funkt dein Script nicht mehr.

    --
    Lg,
    Snafu
    1. Hm.

      Wenn jemand JS abgedreht hat dann Funkt dein Script nicht mehr.

      Aber auf den kleinen Anteil kann ich glaub ich verzichten ich weiß nicht.. =/
      Und sonst?

      1. Aber auf den kleinen Anteil kann ich glaub ich verzichten ich weiß nicht.. =/

        Geht ums Prinzip. Je "komplizierter" desto fehleranfälliger. Schonmal sowas ähnliches gehört?

        --
        Lg,
        Snafu
  2. Hi Logik,

    Beim abschicken: Javascript: Erstelle md5 Hashsumme rufe Dokument auf welches die Klasse "Loginsystem" aufruft

    Was erhoffst du dir davon? Höhere Sicherheit? Die hast du damit definitiv nicht, das ist genauso sicher, als wie wenn das Passwort im Klartext übertragen würde.

    Wenn ich als Angreifer die Verbindung abhöre, dann kriege ich nur die md5-Summe des Passwortes zu lesen und nicht das Passwort im Klartext - aber das reicht mir vollkommen! Um mich als Angreifer unter einem fremden Benutzerkonto anzumelden, brauche ich ja ebenfalls nur die md5-Summe des Passwortes an den Server zu schicken, also genau das, was ich beim Abhören mitgelesen habe.

    class Loginsystem{
    function Login(Name, PW){
      // Nehme Name und PW entgegen
      // erstelle sha1 Hashsumme

    eine sha1-Summe von der md5-Summe? Wozu? Das verkompliziert dein Verfahren lediglich, ohne einen deutlichen Mehrgewinn an Sicherheit zu bringen. Abgesehen davon ist SHA-1 nicht mehr als wirklich sicher zu betrachten, vielleicht solltest du also lieber ein SHA-256 oder etwas ganz anderes vorziehen.

    // Guck in Datenbank nach ob beides vorhanden ist
      // Lese Userid aus
      // Hashwert der Userid (sha1) = HU
      // Erstelle Session und schreibe dort HU rein

    Wozu willst du den Hashwert der Userid in der Session? Das ist doch äußerst unbequem - da kannst du die Userdaten nicht anhand der Userid aus der Datenbank auslesen, dazu müsstest du erst auf alle Userids der Datenbank die Hashfunktion anwenden um den entsprechenden Datensatz zu finden.

    Fortlaufende Zahlen haben die hervorragende Eigenschaft, dass sie eindeutig sind. Die Hashwerte zu zwei von diesen Zahlen können identisch sein - das ist zwar unwahrscheinlich, aber möglich. Warum also dieses Risiko eingehen?

    // Erstelle Cookie und schreibe dort HU rein

    Wozu denn das noch? Der User wird anhand der Session wiedererkannt. Und die Session wird anhand des Session-Cookies erkannt. Wozu willst du also noch ein Cookie mit dem Usernamen?

    Und warum schreibst du hier nur den Hash des Usernamens rein? Erhoffst du dir hier mehr Sicherheit? Auch hier muss ich sagen: Nein. Sofern du dieses Cookie benutzt um durch eine Zugangskontrolle zu kommen, so brauche ich als Angreifer lediglich die Verbindung abzuhören und schon habe ich den Hashwert, ich weiß nicht wieder User heißt, aber das muss ich auch nicht wissen, wenn der Hashwert des Usernamens bereits vollkommen ausreicht um durch die Zugangskontrolle zu kommen.

    // Schreibe in die Datenbank aktuelles Datum und Uhrzeit rein + HU

    Du hast die Userid aus der Datenbank ausgelesen und willst du die gehashte Userid wieder in die Datenbank reinschreiben - Wozu?

    function auth(Cookie, Session){
      // vergleiche HU von Cookie und Session
         // Falls es nicht stimmt, abbrechen#

    Ok, nun verstehe ich warum du mit zwei Cookies arbeiten willst - damit zu zwei Mechanismen hast um zu prüfen ob der User eingeloggt ist.

    Aber warum? Das bringt nichts, wenn ich einmal eine Verbindung abgehört habe, dann habe ich sofort das HU-Cookie, als auch das Session-Cookie, da bei jedem Request vom Browser beide Cookies übermittelt werden müssen. Ergo kannst du problemlos auf ein Cookie kürzen.

    Was du eigentlich willst, ist SSL zu benutzen, also deine Seiten über https aufzurufen. Alles andere ist nur Trickserei, die den Angreifer vielleicht etwas mehr Zeit kostet, dein System aber nicht sicherer macht, als es ohnehin wäre.

    Vielleicht könntest du nur den eigentlichen Login-Prozess, also das Übermitteln von Benutzername und Passwort über eine SSL-verschlüsselte Verbindung laufen lassen und alle anderen Seiten über normales, unverschlüsseltes HTTP. Dann könnte man zumindest schon mal die Benutzerdaten nicht abhören.

    Und um Session-Diebstahl zu verhindern beschränkst du dann noch die Lebensdauer einer Session auf 20 Minuten o.ä. bzw. beendest die Session automatisch nach 5 Minuten Inaktivität. Interessant kann auch die Idee sein, bei jedem Aufruf eine neue Session-ID zu generieren (session_regenerate_id()), wird dies sauber implementiert, so ist ein Session-Diebstahl bereits sehr schwer und nur so lange möglich, wie der eigentliche User noch keinen weiteren Request nach dem erfolgreich abgehörten Request vorgenommen hat.

    Viele Grüße,
      ~ Dennis.

    1. Hmm.
      Okay da hast du Recht
      nur bin ich jetzt so verwirrt das ich garnicht mehr weiß wie das ganze nun ablaufen soll.
      Könntest du mal deine Version von den Funktionen login(), auth(), logout()
      hier schreiben?
      Danke für deine Bemühungen.

      Liebe Grüße, die Logik

      1. Hallo!

        Okay da hast du Recht
        nur bin ich jetzt so verwirrt das ich garnicht mehr weiß wie das ganze nun ablaufen soll.

        Hast du dir das Beispiel auf Selfhtml.org schon angesehen?
        Dort müsstest du eigentlich nur an der Stelle wo Benutzername und Passwort überprüft werden gegen eine MySQL DB prüfen.

        mfg
          frafu

        1. Hi.

          Hast du dir das Beispiel auf Selfhtml.org schon angesehen?
          Dort müsstest du eigentlich nur an der Stelle wo Benutzername und Passwort überprüft werden gegen eine MySQL DB prüfen.

          Ja kenn ich bereits!

          Ich weiß nicht, ist das sicher genug?

          Lg

          1. Hallo!

            Ich weiß nicht, ist das sicher genug?

            Sicher genug wofür? Sicher genug für ein internes Diskussionsforum für den Kegelklub Hintertupfing: ja.
            Sicher genug für die Steuerung von Atomraketenabschußrampen: Nein

            mfg
              frafu

          2. Hi Logik,

            Ich weiß nicht, ist das sicher genug?

            Dieses Loginsystem ist effektiv genauso sicher wie das von dir programmierte System - das habe ich mit meinem vorherigen Posting ja bereits versucht zu erklären.

            Durch Abhören der Kommunikation zwischen dem Besucher und deinem Server kann man sowohl dein System, als auch das auf SELFHTML Vorgestellte umgehen. Dem kannst du nur entgegenwirken, indem du eine SSL-verschlüsselte Verbindung (https:// statt http://) verwendest - solange du das nicht tust 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.

            Viele Grüße,
              ~ Dennis.

            1. Hallo Dennis,

              Durch Abhören der Kommunikation zwischen dem Besucher und deinem Server kann man sowohl dein System, als auch das auf SELFHTML Vorgestellte umgehen. Dem kannst du nur entgegenwirken, indem du eine SSL-verschlüsselte Verbindung (https:// statt http://) verwendest - solange du das nicht tust 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.

              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 - natürlich kann weiterhin mitgelesen werden, welche Daten Du transferierst, aber es gibt Fälle, in denen lediglich der bloße Zugang gesichert werden muss. 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.

              Vielleicht sollte ich mal einen Artikel zu dem Thema schreiben...

              Viele Grüße,
              Christian

              1. Hi Christian,

                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*

                …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.)

                Wenn der Angreifer einen späteren Request mitschneidet, dann bekommt er nur die Session-ID, welche er benutzen kann, bis die Session abläuft.

                Dem ersten Fall kannst du nur mit SSL entgegenwirken, letzterem Fall mit häufigem session_regenerate_id().

                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).

                Ob sich für ein bestimmtes Projekt der Aufwand der Einrichtung von SSL und die Verwendung von session_regenerate_id() lohnt, muss der Betreiber des Projekts selber entscheiden.

                Viele Grüße,
                  ~ Dennis.

                1. 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.

                  1. Hallo,

                    da blick ich nicht mehr durch!

                    Sag mir bitte Schritt für Schritt was ich nach dem die Daten abgeschickt wurden tun soll und wie auth.php danach aussehen muss.

                    Bitte!!!!

                    Danke im Vorraus.

                    Lg

                    Die Logik ohne Logik!

                  2. Hallo nochmal,

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

                    Ok, das (Zero Knowledge) stimmt nicht, wie mir im Chat gerade mitgeteilt wurde. Challenge-Response erfüllt eine wichtige Eigenschaft davon nicht. Das ändert aber nichts am Grundprinzip davon.

                    Viele Grüße,
                    Christian

                  3. Hi Christian,

                    So, hat etwas gedauert, bis ich über die Karnevals-Tage dazu gekommen bin hier mal zu antworten ;-)

                    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.

                    Ok, daran hatte ich nicht gedacht - meine Aussage „alle Loginsysteme” bezog sich vielmehr nur auf die gebräuchlichen Loginsysteme, zu welchen ich jetzt mal HTTP Basic Auth, sowie ein z.B. in PHP programmiertes, simples Login-System. Diese Systeme stellen alle kein Challenge-Response-Verfahren da und sind somit nicht sicher, solange man sie nicht über SSL/TLS laufen lässt. ;-)

                    Außerdem muss das Passwort im Klartext beim Server vorliegen. […] Muss man sich halt was dafür überlegen. ;-)

                    Ich überlege mir gerade, wie das wohl mit Auth Digest funktioniert. Auth Digest wäre ja vom Prinzip her Challenge-Response… Für den Server bedeutet dies meines Erachtens einen Mehraufwand - herkömmlicherweise kann z.B. Apache ja jeden Request für sich getrennt behandeln. Bei Auth Digest müsste Apache ja aber im Hintergrund eine Art Session führen, da er sich merken muss, welchem Client er welchen Zufallswert rausgegeben hat, oder? Würde mich mal interessieren wie man so etwas realisiert, unter anderem wenn man gar nicht weiß, welcher Prozess oder Thread den zweiten Request des Clients erhält.

                    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 stehe dem Einsatz von Javascript für wichtige Operationen nach wie vor kritisch gegenüber. Meines Erachtens muss gerade ein Login-System auf jeden Fall ohne Javascript funktionieren - aber dann wäre die gewonnene Sicherheit ja futsch.

                    Und Usern, welche Javascript deaktiviert haben, keine Sicherheit bieten? Nun gut, man könnte es auch andersrum sehen: Usern, welche Javascript aktiviert haben zusätzliche Sicherheit bieten! *g* Dürfte letztendlich eine Frage der Philosophie sein, wobei bei ich wie gesagt im Zweifelsfall einfach SSL/TLS wählen würde, mit CAcert und einem Root-Server ist das auch schnell realisiert ;-)

                    Ja, deswegen schrieb ich ja, dass Challenge-Response gegen Live-Angriffe (!) nichts bewirkt [*].

                    Genau, allerdings nur, wenn man eine „Session” verwendet, also einen sich nicht ändernden Schlüssel, mit welchem man Zugang erhält. Das war ja eigentlich auch alles was ich sagen wollte - ich sehe keinen Sinn darin, aufwendig ein Challenge-Response Login-Verfahren zu programmieren mit jeder Menge Javascript auf der Client-Seite, wenn letztendlich im Anschluss daran nur der Session-Schlüssel abgehört werden muss, damit das System geknackt ist.

                    Wenn ich das richtig sehe, ist dies aber im Falle von Auth Digest aber nicht der Fall, oder? Hier müssten doch eigentlich bei jedem Request die Zugangsdaten übermittelt werden, wie bei Auth Basic. Und weil die URL mit als Salz für den Hash genommen wird, ist der übertragene Schlüssel bei jedem Request anders.

                    For subsequent requests, the hexadecimal request counter (nc) must be greater than the last
                      value it used – otherwise an attacker could simply "replay" an old request with the same
                      credentials. It is up to the server to ensure that the counter increases for each of the nonce
                      values that it has issued, rejecting any bad requests appropriately. Obviously changing the
                      method, URI and/or counter value will result in a different response value.

                    Aus dem oben verlinkten Artikel der englischen Wikipedia - dank des Counters wird also verhindert, dass zwei aufeinander folgenden Requests auf die gleiche URI nicht den gleichen Authorisierungs-Schlüssel verwenden. Auch hier überlege ich mir gerade wieder, dass muss sich der Server ja alles in einer Art „Session” im Hintergrund merken… ganz schön aufwendig ;-)

                    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.

                    Stimmt, an den Aspekt habe ich noch gar nicht gedacht, sofern der Server also Session-IDs über GET-Parameter akzeptiert sollte man hier gezielt vorbeugen.

                    […] Wirkliche Sicherheit hat man natürlich nur mit SSL/TLS […]

                    Ok, da sind wir uns einig :-)

                    Viele Grüße,
                      ~ Dennis.

                    1. Hallo Dennis,

                      Ich überlege mir gerade, wie das wohl mit Auth Digest funktioniert. Auth Digest wäre ja vom Prinzip her Challenge-Response…

                      Ja, ist es auch.

                      Für den Server bedeutet dies meines Erachtens einen Mehraufwand - herkömmlicherweise kann z.B. Apache ja jeden Request für sich getrennt behandeln. Bei Auth Digest müsste Apache ja aber im Hintergrund eine Art Session führen, da er sich merken muss, welchem Client er welchen Zufallswert rausgegeben hat, oder?

                      Ja.

                      Würde mich mal interessieren wie man so etwas realisiert, unter anderem wenn man gar nicht weiß, welcher Prozess oder Thread den zweiten Request des Clients erhält.

                      mod_auth_digest nutzt Shared Memory Segmente, um das zu speichern. Übrigens: mod_auth_digest implementiert Digest Auth auch nicht vollständig, es funktioniert zwar (wenn der Browser es kann), aber einige Aspekte der Spezifikation (z.B. MD5-sess) sind nicht implementiert, d.h. alle Features, die in Digest Auth vorgesehen sind, kannst Du nicht nutzen.

                      Und Usern, welche Javascript deaktiviert haben, keine Sicherheit bieten? Nun gut, man könnte es auch andersrum sehen: Usern, welche Javascript aktiviert haben zusätzliche Sicherheit bieten!

                      Ja, darum ging es mir.

                      wobei bei ich wie gesagt im Zweifelsfall einfach SSL/TLS wählen würde, mit CAcert und einem Root-Server ist das auch schnell realisiert ;-)

                      Wie viele Leute, die zum Beispiel ein eigenes Weblog benutzen, verwenden sowas? Bzw. ist es für die praktikabel, sowas zu verwenden?

                      Ja, deswegen schrieb ich ja, dass Challenge-Response gegen Live-Angriffe (!) nichts bewirkt [*].

                      Genau, allerdings nur, wenn man eine „Session” verwendet, also einen sich nicht ändernden Schlüssel, mit welchem man Zugang erhält. Das war ja eigentlich auch alles was ich sagen wollte - ich sehe keinen Sinn darin, aufwendig ein Challenge-Response Login-Verfahren zu programmieren mit jeder Menge Javascript auf der Client-Seite, wenn letztendlich im Anschluss daran nur der Session-Schlüssel abgehört werden muss, damit das System geknackt ist.

                      Naja, sobald man sich ausloggt, ist der Session-Key ja nichts mehr wert. Das heißt: Wenn jemand nicht genau in dem Zeitraum, in dem Du eingeloggt bist, etwas macht, dann nützt ihm der Session-Key auch nichts.

                      Zudem: Wenn Du in der Session die IP-Adresse speicherst, dann kannst Du so etwas größtenteils verhindern (nicht komplett, es gibt immer noch mögliche Angriffe - jedoch *sehr* aufwendig). Das muss allerdings deaktivierbar für den User sein, weil manche User sich zwangsweise hinter ändernden IPs bewegen (z.B. AOL-User mit Proxy).

                      Mit IP-Sperrre bleiben natürlich immer noch Man-in-the-middle-Angriffe (dagegen ist Challenge-Response zwar an sich immun, allerdings kann ja der JS-Code zum Hashen der Zugangsdaten einfach entfernt werden), Man-in-the-middle ist aber weitaus aufwendiger, als bloßes Lauschen.

                      Ist immer die Frage, wogegen man sich absichern will. Stell Dir doch einfach eine typische Weblog- oder Forensoftware vor. Die wenigsten Leute können es sich leisten, den Adminzugang über SSL/TLS zu bewerkstelligen (hey, sogar wir hier bei SELFHTML verwenden kein SSL/TLS für das Forum weil wir den normalen Usern keine Zertifikatswarnung vorsetzen wollen und ein HTTP/HTTPS-Mischmasch extrem verwirrend wäre und nur zu Problemen führen würde). Und klar, HTTP bekommt man nicht vollkommen abgesichert, Man-in-the-middle ist da immer problematisch (wenn man's mit Javascript macht, das man als MITM einfach entfernen kann, wenn der Browser was direkt implementiert natürlich nicht).

                      Ich persönlich stelle mich auf folgenden Standpunkt: Kann man die jetztige Situation irgendwie verbessern? Im Moment ist es nämlich bei Formular-Logins extrem trivial die Auth-Daten abzufangen, gerade in offenen WLANs oder ähnlichem. Und mit einigen Maßnahmen man könnte es einem Angreifer sehr (!) viel schwerer machen, Zugang zum System zu erhalten.

                      Denn: Einfach mal einen Rechner in ein offenes WLAN stellen und Zugangsdaten mitsniffen lassen und sie sich hinterher schön ausgeben lassen ist sehr einfach - gibt's auch Programme dazu. Mit einem Rechner Live das mögliche Eingeben von Zugangsdaten beobachten und sich die Session-ID krallen und während der Zeit, in der der Benutzer eingeloggt ist, etwas zu machen, ist schon aufwendiger. Bei einer IP-Sperre der Session seine eigene IP zu fälschen und den kompletten TCP-Handshake + Request durchzudrücken, bevor der richtige Rechner ein RST-Paket schickt halte ich für wahnsinnig aufwädig. Und Man-in-the-middle halte ich im Normalfall für nicht allzu praktikabel - wobei es hier Ausnahmen gibt (jemand simuliert auf einen Flughafen einen Hotspot der Telekom o.ä. obwohl er eigentlich einen bösartigen Rechner mit transparentem Proxy betreibt etc.).

                      Wenn ich das richtig sehe, ist dies aber im Falle von Auth Digest aber nicht der Fall, oder? Hier müssten doch eigentlich bei jedem Request die Zugangsdaten übermittelt werden, wie bei Auth Basic. Und weil die URL mit als Salz für den Hash genommen wird, ist der übertragene Schlüssel bei jedem Request anders.
                      [+nonce-counter-Zeug]

                      Ja.

                      [Session fixation, session_regenerate_id()]

                      Stimmt, an den Aspekt habe ich noch gar nicht gedacht, sofern der Server also Session-IDs über GET-Parameter akzeptiert sollte man hier gezielt vorbeugen.

                      Nicht nur dort. Es ist auch denkbar, fremde Session-Cookies zu "injecten". Drei Beispiele (gibt vermutlich noch mehr):

                      1. Cookies dürfen nur für Second Level Domains gesetzt werden, damit man keine Cookies für .com etc. setzen kann. Allerdings nützt das nur begenzt etwas:

                      1a. Wenn man aber zum Beispiel Webspace nur als Subdomain hat, z.B. meinname.gratishoster.example, könnte jemand anderes mit einem anderen Account auf gratishoster.example - boesewicht.gratishoster.example - Dir für die gesamte (!) gratishoster.example-Domain einen Cookie setzen, der dann auch an meinname.gratishoster.example gesendet wird - mit der falschen Session-ID.

                      1b. Es gibt ja "Quasi-Toplevel-Domains" wie .co.uk, .com.ar etc. - einige davon stehen bei Browsern bereits auf einer Art Blacklist, andere nicht. Und zumindest bei .com.ar funktioniert das Setzen von Cookies für die gesamte "Quasi-TLD" von einer Domain wie *****.com.ar aus (probier's aus, /etc/hosts ist dabei Dein Freund ;-)).

                      2. Sollte es eine XSS-Lücke in der Seite geben, kann Javascript injeziert werden, das so einen Cookie setzt (der im Idealfall auch noch länger lebt, dass das nichtmal ein Live-Angriff sein muss!). Und selbst wenn die Seite selbst sicher ist, kann auf einer beliebigen (!) Subdomain eine XSS-Lücke bestehen, dann war's das auch schon.

                      Gut, zu 2 wäre noch zu sagen, dass es eine Möglichkeit gibt (geht allerdings nicht in allen Browsern), Cookies vor Javascript abzuschotten, das nützt aber nur dann etwas, wenn der Cookie bereits gesetzt wurde, d.h. es verhindert effektiv nur ein Auslesen eines bestehenden Session-Cookies durch XSS-Attacken - nicht jedoch das *vorherige* Setzen eines eigenen.

                      ----------------------- schnipp ---------------

                      Allgemeines noch zu Challenge-Response (ich hatte auch im Chat eine interessante Diskussion deswegen): Challenge-Response hat den großen Nachteil, dass man sich mit den Informationen, die auf dem Server gespeichert sind, einloggen kann. Das heißt: Taucht die User-DB irgendwann einmal im Internet auf, so kann sich bei Challenge-Response jeder mit den dort vorhandene Informationen einloggen. Selbst wenn die Daten gehasht und gesaltet gespeichert werden: Der Client muss genau diesen Prozess bei sich mit dem gleichen Hash und Salt nachbauen, damit er das gleiche Ergebnis erzeugen kann, wie der Server. Daher ist es bei Challenge-Response im Prinzip relativ sinnlos, die Passwörter zu hashen / salten, weil der Zugang damit trotzdem möglich wird.

                      Henryk hat mich im Chat allerdings auf ein Projekt namens SRP aufmerksam gemacht. Das dort vorgeschlagene Protokoll bewerkstelligt folgendes:

                      a) Das Passwort wandert nie im Klartext über die Leitung.
                      b) Der Server kennt nur ein gehashtes, gesaltetes Passwort, nicht das Klartextpasswort.
                      c) Der Client *muss* das Klartextpasswort kennen, um sich einloggen zu können.
                      d) Der Server kann das Klartextpasswort aus den ihm zur Verfügung stehenden Daten auch nach der Authentifizierung nicht rekonstruieren.
                      e) Der Client kann verifizieren, dass der Server einen korrekten Hash des Passworts kennt.
                      f) Sollte die User-DB des Servers veröffentlicht werden, kann ein Angreifer sich damit nicht einloggen (der Client muss). Er kann natürlich weiterhin Wörterbuchangriffe gegen die Hashes fahren. Allerdings kann er sich dem Client gegenüber als gültiger Server ausgeben (weil er ja jetzt den Hash kennt).
                      g) Man-in-the-middle funktioniert nicht.
                      h) Server- und Client handeln einen gemeinsamen Schlüssel aus, der nur ihnen bekannt ist.

                      Ich werde damit in nächster Zeit mal etwas rumspielen, wollte es nur mal erwähnt haben, vielleicht interessiert's Dich auch.

                      Viele Grüße,
                      Christian

                      1. Hi Christian,

                        Naja, sobald man sich ausloggt, ist der Session-Key ja nichts mehr wert. Das heißt: Wenn jemand nicht genau in dem Zeitraum, in dem Du eingeloggt bist, etwas macht, dann nützt ihm der Session-Key auch nichts.

                        Wobei wir wieder bei dem bereits von mir angesprochenen bzw. angedeuteten Punkt wären, dass die Lebenszeit der Session ausreichend kurz (z.B. max 30 Min) sein sollte und ein User bei Inaktivität frühzeitig (z.B. nach 10 Min) ausgeloggt wird - viele Leute nutzen nämlich den Logout nicht.

                        Ich persönlich stelle mich auf folgenden Standpunkt: Kann man die jetztige Situation irgendwie verbessern? Im Moment ist es nämlich bei Formular-Logins extrem trivial die Auth-Daten abzufangen, gerade in offenen WLANs oder ähnlichem. Und mit einigen Maßnahmen man könnte es einem Angreifer sehr (!) viel schwerer machen, Zugang zum System zu erhalten.

                        Ok, da stimme ich dir zu. Natürlich sollte man solch einfache Angriffsmöglichkeiten verhindern. Zu Beginn dieses Threads habe ich mir allerdings mehr Gedanken zu einem sicheren™ Verfahren gemacht, wie du selber bereits gesagt hast, kommt dafür nur SSL/TLS in Frage - trotzdem wäre es natürlich schön, wenn man auch bei unverschlüsseltem HTTP Transfer etwas Sicherheit bieten könnte, wobei ich nach wie vor bei der Bedingung bleibe, dass gerade ein Login auch ohne Javascript möglich sein *muss*. (Gerne mit entsprechendem Hinweis auf die damit verbundenen Risiken.)

                        Es ist auch denkbar, fremde Session-Cookies zu "injecten". Drei Beispiele (gibt vermutlich noch mehr): […]

                        Danke, dass du das noch mal so ausführlich notiert hast ;-) Ich werde mir es auf jeden Fall merken *g*

                        Allgemeines noch zu Challenge-Response (ich hatte auch im Chat eine interessante Diskussion deswegen): Challenge-Response hat den großen Nachteil, dass man sich mit den Informationen, die auf dem Server gespeichert sind, einloggen kann. Das heißt: Taucht die User-DB irgendwann einmal im Internet auf, so kann sich bei Challenge-Response jeder mit den dort vorhandene Informationen einloggen. Selbst wenn die Daten gehasht und gesaltet gespeichert werden: Der Client muss genau diesen Prozess bei sich mit dem gleichen Hash und Salt nachbauen, damit er das gleiche Ergebnis erzeugen kann, wie der Server. Daher ist es bei Challenge-Response im Prinzip relativ sinnlos, die Passwörter zu hashen / salten, weil der Zugang damit trotzdem möglich wird.

                        Genau, vielleicht ist das auch das Problem, was ich hier mit Challenge-Response habe. Man muss recht großen Aufwand betreiben und hat am Ende doch nicht alles so, wie man es am liebsten hätte.

                        Ich habe zwischenzeitlich auch mal noch etwas recherchiert und zwar mit folgendem Hintergedanken: Warum müssen wir uns komplizierte Verfahren ausdenken, wenn es doch bereits erprobte Algorithmen zur Verschlüsselung gibt? Warum verwenden wir nicht einfach eine asynchrone Verschlüsselung, wo der Server seinen Public-Key mit dem HTML-Dokument ausliefert?
                        Wir haben oben festgehalten, dass auch Challenge-Response gegen Man-In-The-Middle machtlos ist, weil der Javascript-Code zur Berechnung ausgetauscht werden kann. Insofern kann man es einer asynchronen Kryptographie nicht als Nachteil anhängen, dass ein Man-In-The-Middle den Public-Key im HTML-Code beim Übertragen manipulieren könnte.

                        Fehlen nur noch Libraries dafür in Javascript, ich habe mal gesucht und bin auf PGP / GnuPG / OpenPGP Message Encryption in JavaScript gestoßen. Getestet habe ich es auch, ich habe der Seite meinen Public-Key und eine Nachricht gegeben, den verschlüsselten Output der Seite konnte ich dann lokal mit meinem Private-Key wieder erfolgreich entschlüsseln. Funktioniert also alles bestens und die Laufzeit sollte bei einem 1024-Bit Key und einem kurzen zu verschlüsselnden Text wie ein Passwort auch schnell genug sein, um es on-the-fly zu erledigen.

                        Henryk hat mich im Chat allerdings auf ein Projekt namens SRP aufmerksam gemacht. Das dort vorgeschlagene Protokoll bewerkstelligt folgendes:

                        a) Das Passwort wandert nie im Klartext über die Leitung.
                        b) Der Server kennt nur ein gehashtes, gesaltetes Passwort, nicht das Klartextpasswort.
                        c) Der Client *muss* das Klartextpasswort kennen, um sich einloggen zu können.
                        d) Der Server kann das Klartextpasswort aus den ihm zur Verfügung stehenden Daten auch nach der Authentifizierung nicht rekonstruieren.
                        e) Der Client kann verifizieren, dass der Server einen korrekten Hash des Passworts kennt.
                        f) Sollte die User-DB des Servers veröffentlicht werden, kann ein Angreifer sich damit nicht einloggen (der Client muss). Er kann natürlich weiterhin Wörterbuchangriffe gegen die Hashes fahren. Allerdings kann er sich dem Client gegenüber als gültiger Server ausgeben (weil er ja jetzt den Hash kennt).
                        g) Man-in-the-middle funktioniert nicht.
                        h) Server- und Client handeln einen gemeinsamen Schlüssel aus, der nur ihnen bekannt ist.

                        Ich werde damit in nächster Zeit mal etwas rumspielen, wollte es nur mal erwähnt haben, vielleicht interessiert's Dich auch.

                        Klingt sehr interessant, ich habe es mir ebenfalls mal angesehen… verstehe es aber noch nicht so ganz ;-) Ist weiterhin noch ein Login im Klartext möglich? (Für User ohne Javascript)

                        Was heißt Man-In-The-Middle funktioniert nicht? Es funktioniert genauso wie bei Challenge-Response, der Datentransfer kann abgehört werden, das Javascript kann gefälscht werden etc.

                        Im Prinzip sieht mir das so aus, als ob der Client hier entscheidet, ob die Authentifizierung erfolgreich war oder nicht, weil der Client hier den vom Server erhaltenen String mit einem selbst errechneten String vergleicht. Ist das wirklich so? In welchem Praxis-relevanten Beispiel wäre das denn schon akzeptabel?

                        Viele Grüße,
                          ~ Dennis.

                        1. Hallo Dennis,

                          wobei ich nach wie vor bei der Bedingung bleibe, dass gerade ein Login auch ohne Javascript möglich sein *muss*.

                          Klar, bin ich auch absolut dafür.

                          [RSA/PGP mit JS]

                          Wäre eine denkbare Möglichkeit, ja.

                          [SRP]
                          Klingt sehr interessant, ich habe es mir ebenfalls mal angesehen… verstehe es aber noch nicht so ganz ;-) Ist weiterhin noch ein Login im Klartext möglich? (Für User ohne Javascript)

                          Ähm, Du missverstehst mich. ;-) SRP ist genauso wie Challenge-Response ein Verfahren, des erstmal nicht für den Anwendungsfall mit JavaScript und so gedacht ist. Wenn Du auf der SRP-Seite schaust, dann geht es denen hauptsächlich um Programme wie FTP o.ä., die aktuell die Zugangsdaten im Klartext übertragen (die JavaScript-Demo dort ist nur eine Veranschaulichung der Mathematik, hat nichts mit Login zu tun).

                          Man-in-the-middle ist bei SRP genau dann nicht möglich (Challenge-Response genauso), wenn man darauf vertrauen kann, dass der Code von Client und Server nicht manipuliert ist. Das geht bei Javascript natürlich erstmal nicht, das ist klar, da leidet SRP unter den gleichen Problemen wie Challenge-Response. Das Verfahren selbst hat mich halt ziemlich fasziniert und ich hab auch schon ein paar Ideen was man damit nettes im HTTP-Kontext machen könnte. Daher schrieb ich auch, dass ich damit in nächster Zeit etwas rumspielen werde.

                          Viele Grüße,
                          Christian