Ich möchte einer Angular & Spring-Anwendung eine Multi-Faktor-Authentifizierung mit TOTP-Soft-Token hinzufügen und dabei alles so nah wie möglich an den Standardeinstellungen von Spring Boot Security Starter halten .
Die Token-Validierung erfolgt lokal (mit der Aerogear-Otp-Java-Bibliothek), ohne API-Anbieter eines Drittanbieters.
Das Einrichten von Token für einen Benutzer funktioniert, die Überprüfung durch Nutzung von Spring Security Authentication Manager / Providers jedoch nicht.
TL; DR
- Wie kann ein zusätzlicher AuthenticationProvider offiziell in ein mit Spring Boot Security Starter konfiguriertes System integriert werden?
- Welche Möglichkeiten werden empfohlen, um Wiederholungsangriffe zu verhindern?
Lange Version
Die API verfügt über einen Endpunkt, /auth/token
von dem das Frontend ein JWT-Token erhalten kann, indem Benutzername und Kennwort angegeben werden. Die Antwort enthält auch einen Authentifizierungsstatus, der entweder AUTHENTICATED oder PRE_AUTHENTICATED_MFA_REQUIRED sein kann .
Wenn der Benutzer MFA benötigt, wird dem Token eine einzige erteilte Berechtigung von PRE_AUTHENTICATED_MFA_REQUIRED
und eine Ablaufzeit von 5 Minuten ausgestellt. Auf diese Weise kann der Benutzer auf den Endpunkt zugreifen, auf dem er /auth/mfa-token
den TOTP-Code über seine Authenticator-App bereitstellen und das vollständig authentifizierte Token für den Zugriff auf die Site erhalten kann.
Anbieter und Token
Ich habe meine Gewohnheit erstellt, MfaAuthenticationProvider
die implementiert AuthenticationProvider
:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// validate the OTP code
}
@Override
public boolean supports(Class<?> authentication) {
return OneTimePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
Und a, OneTimePasswordAuthenticationToken
das AbstractAuthenticationToken
den Benutzernamen (aus dem signierten JWT) und den OTP-Code enthält.
Konfig
Ich habe meine Gewohnheit WebSecurityConfigurerAdapter
, wo ich meine Gewohnheit AuthenticationProvider
über hinzufüge http.authenticationProvider()
. Nach JavaDoc scheint dies der richtige Ort zu sein:
Ermöglicht das Hinzufügen eines zusätzlichen AuthenticationProviders
Die relevanten Teile von mir sehen so SecurityConfig
aus.
@Configuration
@EnableWebSecurity
@EnableJpaAuditing(auditorAwareRef = "appSecurityAuditorAware")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
public SecurityConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authenticationProvider(new MfaAuthenticationProvider());
http.authorizeRequests()
// Public endpoints, HTML, Assets, Error Pages and Login
.antMatchers("/", "favicon.ico", "/asset/**", "/pages/**", "/api/auth/token").permitAll()
// MFA auth endpoint
.antMatchers("/api/auth/mfa-token").hasAuthority(ROLE_PRE_AUTH_MFA_REQUIRED)
// much more config
Regler
Das AuthController
hat das AuthenticationManagerBuilder
eingespritzt und zieht alles zusammen.
@RestController
@RequestMapping(AUTH)
public class AuthController {
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.tokenProvider = tokenProvider;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@PostMapping("/mfa-token")
public ResponseEntity<Token> mfaToken(@Valid @RequestBody OneTimePassword oneTimePassword) {
var username = SecurityUtils.getCurrentUserLogin().orElse("");
var authenticationToken = new OneTimePasswordAuthenticationToken(username, oneTimePassword.getCode());
var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
// rest of class
Das Posten gegen /auth/mfa-token
führt jedoch zu folgendem Fehler:
"error": "Forbidden",
"message": "Access Denied",
"trace": "org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for de.....OneTimePasswordAuthenticationToken
Warum holt Spring Security meinen Authentifizierungsanbieter nicht ab? Das Debuggen des Controllers zeigt mir, dass dies DaoAuthenticationProvider
der einzige Authentifizierungsanbieter ist AuthenticationProviderManager
.
Wenn ich meine MfaAuthenticationProvider
als Bean ausstelle, ist dies der einzige registrierte Anbieter, sodass ich das Gegenteil erhalte:
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken.
Also, wie bekomme ich beides?
Meine Frage
Was ist die empfohlene Methode, um ein zusätzliches System AuthenticationProvider
in ein von Spring Boot Security Starter konfiguriertes System zu integrieren, damit ich sowohl das DaoAuthenticationProvider
als auch mein eigenes benutzerdefiniertes System erhalte MfaAuthenticationProvider
? Ich möchte die Standardeinstellungen von Spring Boot Scurity Starter beibehalten und zusätzlich einen eigenen Provider haben.
Verhinderung von Wiederholungsangriffen
Ich weiß, dass der OTP-Algorithmus selbst nicht innerhalb der Zeitscheibe, in der der Code gültig ist, vor Wiederholungsangriffen schützt. RFC 6238 macht dies deutlich
Der Prüfer darf den zweiten Versuch des OTP NICHT akzeptieren, nachdem die erfolgreiche Validierung für das erste OTP durchgeführt wurde, wodurch die einmalige Verwendung eines OTP sichergestellt wird.
Ich habe mich gefragt, ob es einen empfohlenen Weg gibt, Schutz zu implementieren. Da die OTP-Token zeitbasiert sind, denke ich darüber nach, die letzte erfolgreiche Anmeldung im Benutzermodell zu speichern und sicherzustellen, dass es nur eine erfolgreiche Anmeldung pro 30 Sekunden Zeitscheibe gibt. Dies bedeutet natürlich eine Synchronisation auf dem Benutzermodell. Irgendwelche besseren Ansätze?
Vielen Dank.
- -
PS: Da es sich um eine Sicherheitsfrage handelt, suche ich nach einer Antwort aus glaubwürdigen und / oder offiziellen Quellen. Vielen Dank.