Was hat sich geändert?
Wer bislang zur Speicherung der Passwörter in Spring Security eine Digest-basierte Speicherung nutzte (z.B. ShaPasswordEncoder
oder MessageDigestPasswordEncoder
), wird bei der Migration auf Spring Security 5 über das neue Speicherformat für Passwörter stolpern:
{id}encodedPassword
Die id gibt dabei den zu verwendenden PasswordEncoder
an. Beispielsweise ist {bcrypt} mit dem BCryptPasswordEncoder
kodiert und {noop} wäre ein Passwort im Klartext.
Eine schöne historische Übersicht und die Begründung warum die SHA basierten Kodierungen inzwischen unsicher sind, bietet die Dokumentation.
Digest-basierte Verfahren sind unsicher
Bislang galt die "Verschlüsselung" der Benutzer-Passwörter mit SHA-256 und einem zufälligen "Salt" als der Stand der Dinge. Der SHA Algorithmus soll schnell einen Hash ohne Kollision erstellen. Wegen der schnellen Operation ermöglicht dies aber auch Brute-Force-Attacken. Ein Algorithmus der diese Operation aufwändiger macht, ist daher zur Speicherung von Passwörtern besser geeignet. bcrypt wurde zu diesem Zweck entwickelt.
Migration SHA-256
Wer bislang den ShaPasswordEncoder
nutzte, braucht bei der Migration lediglich ein Prefix vor das gespeicherte Passwort setzen, und den verwendenten Hash-Algorithmus angeben:
UPDATE "user" SET password = CONCAT('{SHA-256}', password);
Der DelegatingPasswordEncoder
nutzt dieses Prefix und leitet das kodierte Passwort an den MessageDigestEncoder
weiter.
Migration SHA-256 mit Salt
Eine übliche Kombination ist die Kodierung mit dem ShaPasswordEncoder
und ein Salt, der mit dem Benutzer in der Datenbank gespeichert wird. Auf diesen greift der Encoder über die ReflectionSaltSource
zu. Der neue MessageDigestPasswordEncoder
ist in der Lage den Salt zu nutzen.
Dazu muss der Salt in geschweiften Klammern vor das Passwort gesetzt werden:
String s = salt == null ? null : "{" + salt + "}"; String migratedPassword = s + user.getPassword();
Damit wird das Passwort gemeinsam mit dem Salt nun im Passwort-Feld des Benutzers gespeichert. Um jetzt zusätzlich den eingesetzten Hash-Algorithmus austauschbar zu halten, muss auch noch die Information über den eingesetzten Hash-Algorithmus mit in dem Attribut gespeichert werden.
Beispiel:
String password = "thePassword"; String salt = "theSalt"; String migratedPassword = "{SHA-256}{theSalt}thePassword";
Eine Datenbankmigration könnte so aussehen. Die Spalten in der Tabelle user sollen password bzw. salt sein.
UPDATE "user" SET password = CONCAT('{SHA-256}{', salt, '}', password);
Der DelegatingPasswordEncoder
wertet {SHA-256} aus und delegiert an den MessageDigestPasswordEncoder("SHA-256")
weiter. Dieser wertet nun den Salt in den geschweiften Klammern aus und kann so das kodierte Passwort dekodieren.