Security fixes don’t always go as planned. In early 2025, a patch meant to address a separate vulnerability (CVE-2025-22228) in a widely-used authentication framework had an unexpected side effect. It quietly broke the timing attack mitigation in the DaoAuthenticationProvider class—a mistake now documented as CVE-2025-22234.

This article explains what happened, why it’s important, and how attackers can exploit the situation. We’ll also see code snippets to help you check if you’re at risk and review what mitigation steps you can take.

What Was Fixed (and Broken)

CVE-2025-22228 was a routine security fix. But its patch inadvertently changed how DaoAuthenticationProvider checks credentials. The original, secure implementation made sure that authentication checks took about the same time—no matter if the username existed or not.

This constant timing is crucial. Without it, an attacker can infer valid usernames, or other authentication behaviors, by measuring tiny differences in how quickly the server responds.

After the CVE-2025-22228 patch, this constant-time check was broken under some configurations. Suddenly, response times for *non-existent* usernames became noticeably faster, while existing usernames still went through password hashing. This timing gap is all an attacker needs for a *username enumeration* attack.

The attacker just looks for slower responses—they’ve found real usernames!

The risk gets worse if login error messages are generic (e.g., “Bad Credentials”), because timing is the only clue an attacker needs.

Code Snippet: The Problem in DaoAuthenticationProvider

Here’s a simplified look at what might have changed. The original, safe code might have looked like this:

// Safe timing mitigation (before CVE-2025-22228)
UserDetails user;
try {
    user = userDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundException ex) {
    // Always do password hash, even if user doesn't exist
    passwordEncoder.matches(presentedPassword, BLANK_PASSWORD_HASH);
    throw new BadCredentialsException("Bad credentials");
}

// Normal password check for valid user
if (!passwordEncoder.matches(presentedPassword, user.getPassword())) {
    throw new BadCredentialsException("Bad credentials");
}

After the problematic patch, developers accidentally removed the dummy password hashing, making username lookups quick if they don’t exist:

// Vulnerable code (after CVE-2025-22228)
UserDetails user;
try {
    user = userDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundException ex) {
    // No dummy hash - server returns error instantly
    throw new BadCredentialsException("Bad credentials");
}

// Check password as usual if user exists
if (!passwordEncoder.matches(presentedPassword, user.getPassword())) {
    throw new BadCredentialsException("Bad credentials");
}

*This small change opens a big hole for attackers!*

Let’s see how this would play out in real life, using a simple Python script

import requests
import time

url = "https://target-app.com/login";
usernames = ['admin', 'user1', 'fakeuser', 'bob', 'alice']
password = "wrongpassword"

for username in usernames:
    data = {'username': username, 'password': password}
    start = time.perf_counter()
    r = requests.post(url, data=data)
    elapsed = time.perf_counter() - start
    print(f"Username: {username}, Response Time: {elapsed:.4f}s")

If you graph the response times, the valid usernames will almost always take longer to check than invalid ones, even by just 30-100 milliseconds—more than enough for modern attackers to be sure.

*Real-world exploit kits run thousands of guesses and aggregate the timing data, easily pulling valid usernames out of the noise.*

User Enumeration: Attackers can get lists of valid usernames, emails, or account IDs.

- Brute Force Facilitation: With valid usernames, attackers can target password guessing or phishing much more efficiently.
- No Log Entries: Since failed login attempts are normal traffic, intrusion detection may not notice.

Who's Affected?

- Any app using DaoAuthenticationProvider, or its equivalents, with the vulnerable fix applied for CVE-2025-22228.

What Should You Do?

1. Check your implementation. If your DaoAuthenticationProvider doesn’t always hash a password—real or dummy—you’re vulnerable.

References & More Reading

- Original CVE-2025-22234 at NIST NVD (placeholder link)
- Spring Security Docs - Timing Attack Mitigation
- How Timing Attacks Work
- CVE-2025-22228 Patch Notes (placeholder link)

Conclusion

The lesson from CVE-2025-22234 is simple: fixes have consequences, and even a good patch can create a new vulnerability if not carefully reviewed. If you’re responsible for authentication systems, always ensure password checks are time-constant and never leak information—even accidentally.

Stay patched, test your defenses, and don’t let timing betray your users.

*Exclusive research & writing: [YourName], 2024. Share with attribution.

Timeline

Published on: 01/22/2026 21:02:23 UTC
Last modified on: 01/26/2026 15:04:14 UTC