Published: June 2024
Severity: High

Read time: 10 minutes

Spring Security is one of the most widely used libraries for securing Java web applications. However, even the best tools can have vulnerabilities. One such serious flaw—CVE-2024-22257—was discovered in several Spring Security releases, exposing apps to broken access control when a specific voting mechanism is misused.

This article breaks down what this CVE means, how it can be exploited in real-world applications, and what you need to do to stay safe. If you’re a Java developer, or you maintain systems built on Spring Security, keep reading.

What Is CVE-2024-22257?

CVE-2024-22257 is a vulnerability in Spring Security where certain versions allow a dangerous situation: if your code calls AuthenticatedVoter#vote() with a null Authentication object, access may be incorrectly granted. This breaks the fundamental rule of access control—never grant permission unless you're absolutely sure who the user is!

6.2.x prior to 6.2.3

If you’re using one of these versions and your code directly uses AuthenticatedVoter#vote (not through the standard configuration), your app could grant unauthorized access to restricted resources.

Understanding the AuthenticatedVoter Mechanism

Spring Security uses a voting-based approach for access decisions. Each "Voter" looks at the incoming authentication/authorization data and says "grant," "deny," or "abstain."

A simplified structure

public interface AccessDecisionVoter<S> {
    int ACCESS_GRANTED = 1;
    int ACCESS_ABSTAIN = ;
    int ACCESS_DENIED = -1;

    int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
}

AuthenticatedVoter checks if the user is authenticated based on the provided Authentication object. But what happens if you pass null as the authentication parameter?

Let's see how the vulnerable code can look

AuthenticatedVoter voter = new AuthenticatedVoter();
// authentication is null!
int result = voter.vote(null, someObject, someAttributes);

// result is 1 (ACCESS_GRANTED) instead of -1 (ACCESS_DENIED)

Due to the bug in the affected versions, passing null as the Authentication does NOT properly deny access—instead, it may grant it.

Vulnerable Scenario Example

Suppose you have a custom access control in your Spring-based REST API, and you directly use the AuthenticatedVoter:

@Autowired
AuthenticatedVoter voter;

public boolean checkAccess(HttpServletRequest request) {
    // Let's assume getAuthentication sometime returns null!
    Authentication authentication = getAuthentication(request);
    int vote = voter.vote(authentication, request, getConfigAttributes(request));
    return vote == AccessDecisionVoter.ACCESS_GRANTED;
}

If getAuthentication(request) returns null (for example, when someone isn’t logged in), you would expect access to be denied. Instead, due to the bug, it could be granted.

Attackers can exploit this by calling endpoints without authenticating.

Original References

- Pivotal/Spring Security CVE-2024-22257 Security Advisory
- NVD CVE-2024-22257 Entry
- Spring Security 5.7.12 Release Notes
- Spring Security 6.2.3 Release Notes

What Can Attackers Do?

If your app directly uses AuthenticatedVoter#vote() and passes a potentially-null authentication (for example, when the user is not logged in), attackers can bypass authentication completely for routes protected by this voter. They simply omit credentials, and due to the bug, they might still get "ACCESS_GRANTED."

Simple Exploit Flow

1. Attacker identifies an endpoint where access is checked by your custom code using AuthenticatedVoter#vote().

Let's say the endpoint /admin/deleteUser is supposed to be protected

if (checkAccess(request)) {
    // Dangerous operation here
    deleteUser(request.getParameter("userId"));
}

But due to the bug in checkAccess, even if nobody is logged in, this code lets unauthenticated users delete any user account.

Maven

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>6.2.3</version> <!-- or the latest fixed version for your line -->
</dependency>

Gradle

implementation 'org.springframework.security:spring-security-core:6.2.3'

If you must implement custom voting logic, always explicitly check for a valid Authentication

if (authentication == null || !authentication.isAuthenticated()) {
    return AccessDecisionVoter.ACCESS_DENIED;
}

Never trust library behaviors when passing null unless documentation or code review confirms safe denial.

Summary Table

| Spring Security Version | Vulnerable | Fixed In |
|------------------------|------------|------------|
| 5.7.x | Yes | 5.7.12 |
| 5.8.x | Yes | 5.8.11 |
| 6..x | Yes | 6..9 |
| 6.1.x | Yes | 6.1.8 |
| 6.2.x | Yes | 6.2.3 |

Check your version: Upgrade if using any affected version.

- Audit your code: If you use custom access decision logic and interact with AuthenticatedVoter, ensure no nulls are passed for authentication.
- Monitor advisories: Spring Security is critical infrastructure—subscribe to the Spring Security blog.

Want to Learn More?

- Understanding Spring Security's Voter System
- Official Spring Security Samples

Timeline

Published on: 03/18/2024 15:15:41 UTC
Last modified on: 04/19/2024 07:15:09 UTC