Date: June 2024
Severity: High
Systems Affected: Spring Security 6.4., 6.4.1, 6.4.2, 6.4.3
Component: Method Security Annotations on Parameterized Types or Methods
Exploit: Authorization Bypass
Overview
A new vulnerability—CVE-2025-22223—was recently uncovered in Spring Security. The bug affects versions 6.4. through 6.4.3, specifically applications using @EnableMethodSecurity and method security annotations (like @PreAuthorize, @Secured, etc.) on parameterized types or methods.
This issue allows certain methods to bypass authorization checks if their security annotations are not directly attached or are used in generic contexts. If you are not using @EnableMethodSecurity, or if all your secured methods attach annotations to the methods directly (not the class or interface), you are not affected.
Root Cause
Spring Security performs inspection of annotations to determine which methods require authorization checks. In certain cases—like generic (parameterized) interfaces, or parameterized methods—Spring Security 6.4.-6.4.3 fails to find and enforce security annotations.
So, protected code can be called without required permissions.
Code Example
Here's a typical way someone might secure a generic (parameterized) repository interface, expecting it to block unauthorized users:
// A parameterized interface with a security annotation
@PreAuthorize("hasRole('ADMIN')")
public interface SecureRepository<T> {
T findById(Long id);
}
Implementing the interface
// Concrete class
public class UserRepository implements SecureRepository<User> {
@Override
public User findById(Long id) {
// logic here
}
}
Expected behavior:
Only users with ADMIN can call findById.
Actual behavior (if affected):
*Any* authenticated or even unauthenticated user (depending on configuration) may be able to call findById without any block.
Proof of Concept (PoC)
Below is a reduced sample showing a bypass in action.
// Will NOT be picked up by method security in 6.4.-6.4.3
@PreAuthorize("hasRole('ADMIN')")
public interface CrudService<T> {
T get(Long id);
}
// Implementation does NOT inherit annotation enforcement
@Service
public class ProductService implements CrudService<Product> {
@Override
public Product get(Long id) {
// Sensitive operation
return new Product();
}
}
Tests—from a user *without* ADMIN
@Autowired
private ProductService products;
@Test
void testUnauthorizedAccess() {
Product p = products.get(123L); // Succeeds even without ADMIN
}
You do not use method security annotations on parameterized types or generic methods.
- All your method security annotations are attached directly to concrete (non-parameterized) methods.
How to Fix
Best Fix:
→ Upgrade to Spring Security 6.4.4 (or later) when available.
→ Official announcement link (to be updated)
Workaround
- Move method security annotations from the *type* (interface/class) level to the actual, concrete target method implementations.
Example
public interface SecureRepository<T> {
T findById(Long id);
}
public class UserRepository implements SecureRepository<User> {
@Override
@PreAuthorize("hasRole('ADMIN')")
public User findById(Long id) {
// Now properly secured!
}
}
References
- Official CVE entry
- Spring Security Documentation — Method Security
- Spring Blog / Security Announcements
- GitHub Issue Tracker - Example report (TBD exact link)
Conclusion
CVE-2025-22223 makes it possible for attackers to sidestep access controls on protected methods when method security annotations are used on parameterized types or methods. Until a patch is available, the best practice is to attach all method security annotations directly to the actual methods, not generalized interfaces or classes.
Check your codebase, search for method security on generics, and update your Spring Security library as soon as a fix ships.
Stay safe!
> _If you found this guide helpful, share it with your dev team or let us know your experience fixing CVE-2025-22223 in your stack!_
Timeline
Published on: 03/24/2025 18:15:22 UTC
Last modified on: 03/27/2025 16:45:46 UTC