Skip to content

Commit 50e6b65

Browse files
committed
Document DaoAuthenticationProvider timing attack mitigation
Note the user-not-found dummy PasswordEncoder path and the mixed-hash limitation in the DaoAuthenticationProvider and exploits docs. Closes gh-19082
1 parent 09a3280 commit 50e6b65

4 files changed

Lines changed: 31 additions & 0 deletions

File tree

docs/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
*** xref:servlet/exploits/headers.adoc[]
111111
*** xref:servlet/exploits/http.adoc[]
112112
*** xref:servlet/exploits/firewall.adoc[]
113+
*** xref:servlet/exploits/timing.adoc[Timing Attacks]
113114
** xref:servlet/integrations/index.adoc[Integrations]
114115
*** xref:servlet/integrations/concurrency.adoc[Concurrency]
115116
*** xref:servlet/integrations/localization.adoc[Localization]

docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,14 @@ image:{icondir}/number_4.png[] `DaoAuthenticationProvider` uses the xref:servlet
2121

2222
image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService` and a set of authorities containing at least `FACTOR_PASSWORD`.
2323
Ultimately, the returned `UsernamePasswordAuthenticationToken` is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
24+
25+
[[servlet-authentication-daoauthenticationprovider-timing]]
26+
== Timing Attack Mitigation
27+
28+
When a username is not found, `DaoAuthenticationProvider` still performs password validation work against an internally generated password by using the configured `PasswordEncoder`.
29+
This keeps the not-found path closer in duration to the path taken when a user exists, which mitigates username enumeration through timing differences.
30+
31+
Because the same `PasswordEncoder` is used for both paths, the not-found and found paths typically take the same order of magnitude of time when passwords share one encoding algorithm.
32+
If the credential store contains multiple password hash algorithms, those found paths can differ substantially in cost, so a single not-found duration cannot match every found case.
33+
Upgrading stored passwords to a modern algorithm reduces that gap.
34+
See xref:servlet/exploits/timing.adoc#servlet-exploits-timing[Timing Attacks] for more detail.

docs/modules/ROOT/pages/servlet/exploits/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
:page-section-summary-toc: 1
44

55
This section discusses Servlet specific support for xref:features/exploits/index.adoc#exploits[Spring Security's protection against common exploits].
6+
It also covers xref:servlet/exploits/timing.adoc#servlet-exploits-timing[timing attack] mitigation for username and password authentication.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[[servlet-exploits-timing]]
2+
= Timing Attacks
3+
4+
A timing attack observes how long an operation takes in order to learn something about secret state.
5+
For username and password authentication, a common concern is username enumeration: if authenticating with an unknown username is much faster than authenticating with a known username (because password checking is skipped), an attacker can measure response times to discover which usernames exist.
6+
7+
[[servlet-exploits-timing-dao]]
8+
== DaoAuthenticationProvider
9+
10+
xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] mitigates this by still invoking the configured xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] when the username is not found.
11+
It encodes an internally generated password once and, on the not-found path, calls `PasswordEncoder.matches` with the presented password against that value.
12+
The encoder is the same one used for real users, so the not-found path and a found path that uses that encoder typically take the same order of magnitude of time.
13+
14+
There are limits to when the same timing is plausible.
15+
If a credential store contains more than one kind of password hash, found users can take orders of magnitude different amounts of time depending on which algorithm their stored password uses.
16+
Spring Security cannot anticipate a single not-found duration that matches every found scenario in that case.
17+
The best defense is to upgrade user passwords to a modern password algorithm so that validation cost is consistent.
18+
See xref:features/authentication/password-storage.adoc#authentication-password-storage-dpe[`DelegatingPasswordEncoder`] for migrating encodings over time.

0 commit comments

Comments
 (0)