CIT: (PHP) Qualität meines Login-Systems

Guten Tag,

ich bin CIT und bin durch Empfehlung hier gelandet.

Ich würde euch gerne mein Login-System(in PHP) vorstellen und hoffe ich bekomme reichlich Feedback.

Zur Grundlage:
Die Passwörter sind 2x MD5 Gehasht + Salt in einer MySQL Datenbank
Eine statische Variable $logged = false.

1. Der User gibt in ein Formular seinen Usernamen oder seine E-Mail und danach sein Passwort ein.

2. Danach wird das ganze mit einer MySQL Datenbank abgeglichen.

3. Stimmen die Daten überein passiert folgendes:

  • Die UserID wird ausgelesen

  • Die UserID, die Gültigkeitsdauer und ein Salt werden mit einem Trennzeichen implodiert. Das KOnstrukt sieht Pseudo z.B. so aus: USERID/SALT/EXPIRATION

  • Das Konstrukt wird per MCRYPT Blowfish im CFB-Modus verschlüsselt.

  • Das verschlüsselte Konstrukt wird dann in einem Cookie gespeichert, die Verfallszeit des Cookies ist die, welche auch im Konstrukt steckt.

Die Authentifizierung läuft so ab:

  • Das Cookie wird ausgelesen
  • Das Konstrukt entschlüsselt
  • Ist der Salt der richtige? Wenn ja - okay
  • Ist EXPIRATION < time() ? Wenn nein - okay
  • Ist eine UserID vorhanden? Wenn ja - okay
  • setze die statische Variable $logged = true;[1]
  • setze die statische Variable $uid = UserID;[1]

Was haltet ihr davon, habt ihr noch ein paar Anregungen?
Es ist also ein Cookie-basiertes Login-System.

[1] Falls ihr euch fragt warum eine statische Variable: Manchmal will eine Funktion wissen ob jemand eingeloggt ist oder nicht, da ich somit komplett auf Sessions verzichten kann, tue ich das so.

Lieben Gruß,

CIT

  1. hi,

    Was haltet ihr davon, habt ihr noch ein paar Anregungen?

    1. ok, 2. hab ich dazu auch neulich erst einen Artikel geschrieben und diesen mit einem Scrip, was das beschriebene Verfahren demonstriert, ergänzt.

    Interessant ist das secure-Flag für den Cookie, siehe Sicherheitsbetrachtungen im letzten Abschnitt.

    Hotte

    --
    Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
    1. Hallo Hotte.

      Interessant ist das secure-Flag für den Cookie, siehe Sicherheitsbetrachtungen im letzten Abschnitt.

      Was würdest du denn an meinem System noch verbessern?

      Gruß,

      CIT

      1. hi,

        »» Interessant ist das secure-Flag für den Cookie, siehe Sicherheitsbetrachtungen im letzten Abschnitt.

        Was würdest du denn an meinem System noch verbessern?

        Schmeiß die Eigenbau-Authentifizierung raus und verwende einen Server, der für sowas designiert ist, beispielsweise LDAP (1). Dazu gibts Schnittstellen für jede Programmiersprache.

        (1) In einer Firma, wo mehr als eine Handvoll Nutzer zu verwalten sind, stehen bestimmt schon solche Serverchen rum und Du musst das Rad nicht neu erfinden, es sei denn, Dein Auftrag geht in diese Richtung.

        Hotte

        --
        Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
  2. Hi CIT,

    Ich würde euch gerne mein Login-System(in PHP) vorstellen und hoffe ich bekomme reichlich Feedback.

    Gut. Auf gehts.

    Die Passwörter sind 2x MD5 Gehasht + Salt in einer MySQL Datenbank

    Ich denke, erst Salt und dann gehasht waer effizienter ;-)

    Die Authentifizierung läuft so ab:

    • Das Cookie wird ausgelesen
    • Das Konstrukt entschlüsselt
    • Ist der Salt der richtige? Wenn ja - okay
    • Ist EXPIRATION < time() ? Wenn nein - okay
    • Ist eine UserID vorhanden? Wenn ja - okay

    Haaalt. Das ist schlecht. Wenn jemand - warum auch immer - Deinen Salt (und das Format des Cookie-Keys) kennt, dann kann er sich wahllos authentifizieren, ohne sich je einloggen zu muessen. Er muss keinerlei Passwort kennen - Id raten (eine existierende ID zu raten duerfte leicht sein), Salt und ein passendes Expiry Date dran, Cookie produzieren, fertig.

    Wenn Du einen Schuessel in ein Cookie schreibst, dann speichere ihn auch serverseitig. Und von da an zaehlt beim Authentifizieren _nicht_, ob der Schluessel irgendwie in sich Sinn macht, sondern __nur__, ob der Server ihn bereits kennt, weil er ihn hoechstpersoenlich generiert hat.

    Und alle Informationen ueber den User (ID, Expiry) speicherst Du serverseitig dazu. Die haben im Cookie nichts verloren, der Wert des Cookies muss zufaellig (was immer das heisst) sein. Grund: Wenn mir ein abgelaufenes Cookie in die Haende faellt, ist es damit wertlos.

    In Deiner Konstruktion hingegen kann ich mir ein Cookie nehmen, mir 1000 Jahre Zeit nehmen und es entschluesseln, dann kenne ich den Salt und das Format des Keys und kann ein Cookie selbst erzeugen, das Dein Script als Authentifizierung akzeptiert.

    Was haltet ihr davon, habt ihr noch ein paar Anregungen?

    Noch eine Frage:

    [...] da ich somit komplett auf Sessions verzichten kann [...]

    Warum willst Du das?

    Viele Gruesse,
    der Bademeister

    1. Hi.

      Ich denke, erst Salt und dann gehasht waer effizienter ;-)

      So ist es auch, habe bei der Formulierung jetzt nicht auf die Reihenfolge geachtet ;).

      »» Die Authentifizierung läuft so ab:
      »» - Das Cookie wird ausgelesen
      »» - Das Konstrukt entschlüsselt
      »» - Ist der Salt der richtige? Wenn ja - okay
      »» - Ist EXPIRATION < time() ? Wenn nein - okay
      »» - Ist eine UserID vorhanden? Wenn ja - okay

      Haaalt. Das ist schlecht.

      Hm okay.

      Also ich würde es dann so machen:

      • Erzuege zufälligen Schlüssel und setze ein Cookie + verschlüsselte ID .
      • Speicher Cookie + Expiration Date + ID in der Datenbank (Damit sich die Datenbank auch leeren kann, wenn die TIMESTAMPS in der Datenbank abgelaufen sind. Denn wenn ich dies erst bei einer fehlgeschlagenen Authent.. dann waren die Keys umsonst in der Datenbank. Man hat 2 Modis entweder man bleibt für die aktuelle Section eingeloggt oder für immer - "Remember Me"-Funktion halt. Man kann das löschen der ungültigen Sitzungen ja per Cronjob erledigen)

      Beim Authentifizieren:

      • Cookie da? Entschlüssel.
      • Cookie in der Datenbank?
      • Sitzung noch gültig?
      • passt die verschlüsselte USERID auch dazu?

      Und okay - ich sollte dann wieder Sessions benutzen um den eingeloggten Status weiterzugeben.

      Noch eine Frage:

      »» [...] da ich somit komplett auf Sessions verzichten kann [...]

      Warum willst Du das?

      Weil ich mir dachte - wenn ich mir das mit den Sessions wirklich komplett spare - gewinne ich nen Tacken an Geschwindigkeit ;). Also dachte ich mir, versuchste irgendwie auf Cookie oder Session(auch wenn diese ebenfalls Session-Cookies setzen) zuverzichten.

      Gruß,

      CIT

        • Erzuege zufälligen Schlüssel und setze ein Cookie + verschlüsselte ID .
        • Cookie da? Entschlüssel.

        Warum?

        Im Cookie steht ein beliebiger Hash, mehr nicht. Meintwegen den Namen deiner Mutter in binärer Form multipliziert mit einer Zufallszahl + den aktuellen Zeitstempel in Millisekunden als und das Ganze dann als 128-bit-Hash in beliebiger länge inklusive Prüfung, ob der Wert schon existiert oder nicht (bei einer 39-stelligen dezimalzahl kann es auch zu Doppelungen kommen).

        Meinetwegen nutzt du auch uniqid().

        Mehr muss der Client nicht wissen - alles andere (die Nutzer ID, den aktuellen User-Agent zum Loginzeitpunkt, die IP-Adresse - was auch immer du haben willst) bleiben IMMER zu jeder Zeit am Server. Nichts wird ver- oder entschlüsselt.

  3. Moin;

    Was haltet ihr davon, habt ihr noch ein paar Anregungen?
    Es ist also ein Cookie-basiertes Login-System.

    Noch eine Anmerkung zur Sicherheit: Trenne strikt den Prozess der Authentifizierung vom Mechanismus des Session-Aufbaus. D.h., der Wert im Cookie sollte _keine_ Information zum Benutzer enthalten und auch _keine_ Information zum Ablauf der Session (diese Dinge gehören auf den Server).

    Der Cookie-Value sollte nicht synchron verschlüsselt sein, sondern mit einer Hash-Funktion, eine Encryption also, die nicht rückgängig gemacht werden kann.

    Viele Grüße,
    Hotte

    --
    Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
    1. Der Cookie-Value sollte nicht synchron verschlüsselt sein, sondern mit einer Hash-Funktion, eine Encryption also, die nicht rückgängig gemacht werden kann.

      Der Cookie-Wert sollte lediglich ein nicht erratbares Identifkationsmerkmal sein - was da drinnen steht, ist unerheblich.+

      Sinnvollerweise hasht man einfach eine Zufallszahl und prüft, ob das Ergebnis bereits an einen anderen Benutzer vergeben wurde.

      Ob das jetzt ein 128-stellige Binärzahl, eine 39-stellige Dezimalzahl oder eine 32-stellige Hexadezimalzahl ist, hat keine Bedeutung. Eingebürgert hat sich aber eine 32-stellige Hexadezimalzahl.

      Man könnte aber genauso eine Zahl mit Basis 62 verwenden (0-9,a-z,A-Z) damit der String im Cookie sehr kurz wird - aber da muss man schon ein arger Sparfuchs sein :)