Ist dies eine sichere Implementierung von E-Mail zum Zurücksetzen des Passworts?

  • Ich entwerfe einen Mechanismus zum Zurücksetzen von Passwörtern, weil die vorhandene Implementierung mir die Hölle schreckt. Mein Ziel ist es, Reset-Codes zu generieren, die wie folgt lauten:

    • Abgelaufen
    • Manipulationssicher
    • Single Use
    • Resistent gegen Wiederholungsangriffe

    Das Format des Reset-Codes, den ich gefunden habe, ist :

     B64( B64( iv ) || B64( E( uid || timestamp ) ) )
     

    Wobei B64 die Base64-Codierung und E die symmetrische Verschlüsselung ist. Der iv ist der Initialisierungsvektor, der für die Verschlüsselung verwendet wird (zufällig generiert), und ist im Klartext. Das || zeigt die Verkettung an und ich verwende tatsächlich die Zeichenfolge "||" als Trennzeichen. uid ist eine undurchsichtige Kennung des Benutzers, damit ich sie in der Datenbank nachschlagen kann.

    Mit timestamp kann ein Code abgelaufen sein.

    Ich verwende AES 256 im Galois-Counter-Modus, der vor modifizierten Chiffretext-Angriffen schützen sollte.

    Der Verschlüsselungsschlüssel ist auf dem Server gespeichert und verwendet, um die Verschlüsselung durchzuführen.

    Das iv wird doppelt verwendet. Wenn der Rücksetzcode generiert wird, wird iv mit dem Benutzer in der Datenbank gespeichert. Dies wird verwendet, um die einmalige Verwendung eines Rücksetzcodes zu erzwingen (er wird gelöscht, wenn ein Rücksetzcode verarbeitet wird) und um die Verwendung von zuvor generierten Rücksetzcodes zu verhindern. Wenn iv in der Datenbank nicht mit dem zum Entschlüsseln des Rücksetzcodes verwendeten iv übereinstimmt, wird die Anforderung abgelehnt. Dies kann auch für die Sicherheit hilfreich sein, falls der Verschlüsselungsschlüssel auf dem Server irgendwie gefährdet ist, da der Angreifer die iv ebenfalls in die Datenbank einfügen muss, damit es funktioniert. Wenn sich der Angreifer auf dem Host befindet, gibt es natürlich einen Toast ...

    Gibt es Schwächen in diesem Design, die ich nicht sehe, oder ist eine weitere Verbesserung erforderlich?

    22 July 2012
    Ilmari Karonen
3 answers
  • Was Sie beschreiben, ist für mich sicher, aber übermäßig kompliziert. Das folgende einfachere Verfahren sollte ausreichen:

    Wenn ein Token zum Zurücksetzen des Kennworts angefordert wird:

    • Erzeugen Sie eine Sicherheit zufälliges Token und speichern Sie es (oder einen kryptografischen Hash davon) zusammen mit einem Zeitstempel in der Datenbank. Überschreiben Sie ggf. das vorherige Token.
    • Senden Sie das Token per E-Mail (entsprechend codiert) an den Benutzer.

    Wenn der Benutzer ein Token sendet:

    • Prüfen Sie, ob es mit dem Token in der Datenbank übereinstimmt und der entsprechende Zeitstempel nicht zu alt ist. < / li>

    Der einzige Teil des Prozesses, an dem Kryptografie überhaupt beteiligt sein muss, ist das Generieren des Zufalls-Token und gegebenenfalls das Hashing. Sie können die frühere Aufgabe normalerweise in das Betriebssystem verlagern. Auf Unixish-Systemen können Sie beispielsweise das Token von /dev/urandom lesen. Wenn Sie eine anständige Kryptobibliothek verwenden, sollte diese alternativ dazu vorgesehen sein, sichere Zufallszahlen zu generieren (die den Betriebssystem-Zufallszahlengenerator intern aufrufen können).


    Ps. Die eine Situation, in der Sie eine krypto-lastigere Lösung wählen möchten, wäre, wenn Sie nicht auf eine bequeme Datenbank zum Speichern der Token zugreifen könnten. In diesem Fall können Sie das Token beispielsweise als MACK(userid || timestamp) berechnen, wobei MACK ein Nachrichtenauthentifizierungscode mit dem geheimen Schlüssel K eingegeben, und der Benutzer muss den Zeitstempel zusammen mit dem Token übergeben. (Sie könnten auch eine Zufallszahl in die MAC-Eingabe aufnehmen, dies ist jedoch nicht unbedingt erforderlich - ohne die Taste K kann ein Angreifer die MAC nicht fälschen.) Da Sie jedoch tun eine Datenbank zur Verfügung haben, dies trifft nicht wirklich zu.

    21 July 2012
    Ilmari Karonen
  • Wie Sie vielleicht wissen, ist gute Krypto nicht einfach, und ausreichend gute Krypto ist noch schwieriger. Um zu beginnen, was auch immer Sie tun, es ist nicht manipulationssicher. Manipulationsresistenz bedeutet, dass der Angreifer einige Werte für seine eigene Agenda nicht manipulieren kann. Die Eigenschaft, nach der Sie suchen, ist die Manipulation der Manipulation, dh die Fähigkeit, festzustellen, ob die gelieferte Nachricht echt ist.

    Zweitens, wie Sie wahrscheinlich mitbekommen haben, benötigen Sie einen Wiedergabeschutz in einem so schönen Protokoll wie http, um den Status zu verfolgen (Nonces passen einfach nicht in HTTP-Befehle mit einer einzigen Anforderung; )) also wird etwas Datenbankpersistenz benötigt. Und wenn Sie bereits etwas beharren müssen, würde ich wirklich Zufallscode in Betracht ziehen und den Code und den Generierungszeitstempel in der Datenbank speichern.

    Ihr Algorithmus ist wirklich gut, wenn er korrekt implementiert wird. Wenn Sie beispielsweise etwas wie "Ungültige iv für Benutzer 'Jane' oder 123456" für manipulierte iv anzeigen, haben wir eine Wirklich groß. Zweitens ist die Validierung;) Ich weiß, dass es für Sie offensichtlich ist, aber für offensichtliche Dinge sind es oft die, die übersehen werden. Also, um das IV zu validieren und die Nachricht zu entschlüsseln, die Gültigkeit des Zeitstempels zu überprüfen und ob die verschlüsselte Benutzer-ID mit derjenigen übereinstimmt in der Datenbank für gegebene iv. Natürlich gibt es auch Probleme mit dem kryptografischen Speicher (der insecure cryptographic-Speicher gehört immerhin zu den Top Ten von owasp). Wenn Sie diese Fragen richtig angehen können, denke ich, werden Sie gut sein.

    Das letzte Wort, wenn Ihre Seite nicht von Milliarden von Benutzern besucht wird oder kein Ziel für einen übermäßig eifrigen Angreifer ist. Sie werden dies tun (Sie verschlüsseln nur bekannte Werte , Benutzer-ID und Zeitstempel werden nicht als geheim betrachtet nofollow "> Unsicherer kryptografischer Speicher und Ross Andersons Security Engineering

    20 July 2012
    damiankolasa
  • Die zwei Hauptprobleme, die ich sehe, sind:

    • B64 (iv || E (uid || timestamp)) ist einfacher und gleichwertiger.
    • uid || timestamp sieht aus, als wäre es anfällig für "kryptografisches Splicing" (außer Sie sagen, dass Sie die UID vom Token wiederherstellen, was in Ordnung sein sollte).
    • Wenn die IV im DB gespeichert ist, muss sie nicht im Token enthalten sein (und sollte möglicherweise nicht im Token sein).

    Unter der Annahme, dass für uid / timestamp keine Vertraulichkeitsanforderungen gelten, besteht der Hauptvorteil der Verwendung der authentifizierten Verschlüsselung darin, dass Sie nicht vergessen können, den MAC zu überprüfen: D () gibt nur einen Fehler zurück. Es gibt immer noch einige sprach- / implementierungsabhängige Probleme:

    • Wenn Sie den Fehler ignorieren, lesen Sie in den meisten C-APIs, was im Ausgabepuffer enthalten ist . (Wenn die Entschlüsselung nicht erfolgt, wird der Ausgabepuffer von einem Angreifer gesteuert!)
    • Wenn ein Entschlüsselungsfehler die leere Zeichenfolge ausgibt und ein Fehlerflag setzt, erhalten Sie möglicherweise int('') == 0 und timestamp('') == timestamp().

    Der Hauptvorteil des Speicherns eines (nicht verschlüsselten) Hash ist, dass Sie sich nicht um die Schlüsselspeicherung / -gefährdung kümmern müssen .

    21 July 2012