Add Compromised Password Checker
Closes gh-7395
This commit is contained in:
@@ -591,3 +591,99 @@ http {
|
||||
======
|
||||
|
||||
With the above configuration, when a password manager navigates to `/.well-known/change-password`, then Spring Security will redirect to `/update-password`.
|
||||
|
||||
[[authentication-compromised-password-check]]
|
||||
== Compromised Password Checking
|
||||
|
||||
There are some scenarios where you need to check whether a password has been compromised, for example, if you are creating an application that deals with sensitive data, it is often needed that you perform some check on user's passwords in order to assert its reliability.
|
||||
One of these checks can be if the password has been compromised, usually because it has been found in a https://wikipedia.org/wiki/Data_breach[data breach].
|
||||
|
||||
To facilitate that, Spring Security provides integration with the https://haveibeenpwned.com/API/v3#PwnedPasswords[Have I Been Pwned API] via the {security-api-url}org/springframework/security/core/password/HaveIBeenPwnedRestApiPasswordChecker.html[`HaveIBeenPwnedRestApiPasswordChecker` implementation] of the {security-api-url}org/springframework/security/core/password/CompromisedPasswordChecker.html[`CompromisedPasswordChecker` interface].
|
||||
|
||||
You can either use the `CompromisedPasswordChecker` API by yourself or, if you are using xref:servlet/authentication/passwords/dao-authentication-provider.adoc[the `DaoAuthenticationProvider]` via xref:servlet/authentication/passwords/index.adoc[Spring Security authentication mechanisms], you can provide a `CompromisedPasswordChecker` bean, and it will be automatically picked up by Spring Security configuration.
|
||||
|
||||
.Using CompromisedPasswordChecker as a bean
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(withDefaults())
|
||||
.httpBasic(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CompromisedPasswordChecker compromisedPasswordChecker() {
|
||||
return new HaveIBeenPwnedRestApiPasswordChecker();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
open fun filterChain(http:HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
authorizeHttpRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
formLogin {}
|
||||
httpBasic {}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun compromisedPasswordChecker(): CompromisedPasswordChecker {
|
||||
return HaveIBeenPwnedRestApiPasswordChecker()
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
By doing that, when you try to authenticate via HTTP Basic or Form Login using a weak password, let's say `123456`, you will receive a 401 response status code.
|
||||
However, just a 401 is not so useful in that case, it will cause some confusion because the user provided the right password and still was not allowed to log in.
|
||||
In such cases, you can handle the `CompromisedPasswordException` to perform your desired logic, like redirecting the user-agent to `/reset-password`, for example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@ControllerAdvice
|
||||
public class MyControllerAdvice {
|
||||
|
||||
@ExceptionHandler(CompromisedPasswordException.class)
|
||||
public String handleCompromisedPasswordException(CompromisedPasswordException ex, RedirectAttributes attributes) {
|
||||
attributes.addFlashAttribute("error", ex.message);
|
||||
return "redirect:/reset-password";
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@ControllerAdvice
|
||||
class MyControllerAdvice {
|
||||
|
||||
@ExceptionHandler(CompromisedPasswordException::class)
|
||||
fun handleCompromisedPasswordException(ex: CompromisedPasswordException, attributes: RedirectAttributes): RedirectView {
|
||||
attributes.addFlashAttribute("error", ex.message)
|
||||
return RedirectView("/reset-password")
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
@@ -8,6 +8,10 @@ Below are the highlights of the release.
|
||||
|
||||
- https://spring.io/blog/2024/01/19/spring-security-6-3-adds-passive-jdk-serialization-deserialization-for[blog post] - Added Passive JDK Serialization/Deserialization for Seamless Upgrades
|
||||
|
||||
== Authentication
|
||||
|
||||
- https://github.com/spring-projects/spring-security/issues/7395[gh-7395] - xref:features/authentication/password-storage.adoc#authentication-compromised-password-check[docs] - Add Compromised Password Checker
|
||||
|
||||
== Authorization
|
||||
|
||||
- https://github.com/spring-projects/spring-security/issues/14596[gh-14596] - xref:servlet/authorization/method-security.adoc[docs] - Add Programmatic Proxy Support for Method Security
|
||||
|
||||
Reference in New Issue
Block a user