CVE-2026-41841
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Framework is the foundational application framework for the Spring ecosystem, providing the core container, web stacks, and integration support used by most Java server-side applications. Its two web stacks, Spring MVC (spring-webmvc, Servlet-based) and Spring WebFlux (spring-webflux, reactive), both serve static resources through a configurable resource-resolver chain. One link in that chain, CachingResourceResolver, caches the Resource it resolves for a given request path so that subsequent requests for the same path can be answered from the cache instead of walking the filesystem or classpath again.
A medium-severity vulnerability (CVE-2026-41841) has been identified in that resolver. When an application registers multiple resource handlers that map overlapping or identical request paths to different locations, share a single resolved-resource cache, and protect at least one of those locations behind authentication or authorization, the cache key does not distinguish between the different location sets. If a request resolves a public resource first, that resource is cached under a key derived only from the request path. A later request that should have resolved the same path against a different, protected location set instead receives a cache hit and is served the previously cached public resource, or the protected resource can be exposed to a context that should not have been able to read it. The net effect is that protected static resources can be disclosed to unauthorized callers under the right caching ordering.
Per OWASP, access control (sometimes called authorization) is how a web application grants access to content and functions to some users and not others, and failures typically lead to unauthorized information disclosure, modification, or destruction of data. Here the failure is an information-disclosure one: a cache key that omits the resource location set lets a lookup intended for a protected location be satisfied from a sibling public location, bypassing the access boundary that the separate location was meant to enforce.
The CVSS v3.1 base score for this vulnerability is 5.9 (Medium) with vector AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N. The attack vector is Network because the resource is requested over HTTP, attack complexity is High because exploitation depends on a specific configuration (multiple resource handlers with overlapping names, a shared cache, and a protected location) and on the public resource being resolved and cached before the protected lookup occurs. No privileges and no user interaction are required, the scope is unchanged, and the impact is Confidentiality-only and High (protected resource content is disclosed); there is no integrity or availability impact because the resolver only reads and serves resources.
This issue affects Spring Framework >=4.3.0 <=4.3.30, >=5.3.0 <=5.3.48, >=6.1.0 <=6.1.27, >=6.2.0 <=6.2.18, and >=7.0.0 <=7.0.7. The caching resource resolver has existed since Spring Framework 4.1 and the affected cache-key logic is present in those earlier lines as well; the advisory range begins at 5.3.0 because that reflects Spring's currently supported lines, not the point at which the issue was introduced.
Details
Module Info
- Product: Spring Framework
- Affected packages: spring-webmvc, spring-webflux
- Affected versions: >=4.3.0 <=4.3.30, >=5.3.0 <=5.3.48, >=6.1.0 <=6.1.27, >=6.2.0 <=6.2.18, >=7.0.0 <=7.0.7
- GitHub repository: https://github.com/spring-projects/spring-framework
- Published packages: https://central.sonatype.com/artifact/org.springframework/spring-webmvc
- Package manager: Maven
- Fixed in:
- NES for Spring Framework 4.3.x, 5.3.x, 6.1.x
- Spring Framework 7.0.8, 6.2.19 (OSS)
Vulnerability Info
The vulnerability is in CachingResourceResolver, which exists in both the Servlet stack (org.springframework.web.servlet.resource.CachingResourceResolver in spring-webmvc) and the reactive stack (org.springframework.web.reactive.resource.CachingResourceResolver in spring-webflux). When the resolver chain is asked to resolve a request path, CachingResourceResolver computes a cache key with computeKey, looks the key up in its Cache, and on a miss delegates to the rest of the chain (which actually searches the configured locations) and stores the result back under the same key.
Pre-fix, computeKey derived the key only from the request path and an optional content-coding suffix. It never considered the locations list that the lookup was performed against:
@Override
protected Resource resolveResourceInternal(@Nullable HttpServletRequest request, String requestPath,
List<? extends Resource> locations, ResourceResolverChain chain) {
String key = computeKey(request, requestPath);
Resource resource = this.cache.get(key, Resource.class);
// ... on hit, return cached resource; on miss, resolve against locations and cache it
}
protected String computeKey(@Nullable HttpServletRequest request, String requestPath) {
if (request != null) {
String codingKey = getContentCodingKey(request);
if (StringUtils.hasText(codingKey)) {
return RESOLVED_RESOURCE_CACHE_KEY_PREFIX + requestPath + "+encoding=" + codingKey;
}
}
return RESOLVED_RESOURCE_CACHE_KEY_PREFIX + requestPath;
}
Because the key is RESOLVED_RESOURCE_CACHE_KEY_PREFIX + requestPath (plus the content coding), two resource handlers that map the same request path to different locations but share one cache will collide on the key. If a public location is resolved first, its Resource is cached under that key; a subsequent lookup that should resolve the same path against a different, access-protected location set gets a cache hit and is served the already-cached resource instead of resolving against its own location. The location set, which is what distinguishes a public handler from a protected one, is invisible to the cache.
Steps To Reproduce
1. Configure a Spring MVC (or WebFlux) application with two static-resource handlers that map an overlapping request path to two different locations, where one location is public and the other is access-protected, and enable the resource chain cache so both handlers share a single resolved-resource cache:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// public assets
registry.addResourceHandler("/assets/**")
.addResourceLocations("classpath:/public-assets/")
.resourceChain(true);
// protected assets, intended to require authentication for the same path space
registry.addResourceHandler("/assets/**")
.addResourceLocations("classpath:/protected-assets/")
.resourceChain(true);
}
Place a file with the same name (for example report.pdf) in both public-assets and protected-assets, and protect the path that should serve from protected-assets behind authentication.
2. As an unauthenticated client, request the path so that it resolves against the public location first. CachingResourceResolver caches the public Resource under a key derived only from the request path (resolvedResource:/assets/report.pdf).
3. Issue the request that should resolve the same path against the protected location set. Because the cache key did not include the locations, the lookup hits the cached public entry (or the previously cached protected entry is served to the public context), and the resolver returns the cached resource without consulting the location set that distinguishes the two handlers.
4. Observe that the protected resource is served, or that the access boundary intended by the separate protected location is not enforced for the cached path.
5. After upgrading to a fixed version (7.0.8, 6.2.19, or a HeroDevs NES build), repeat the steps. The cache key now includes Integer.toHexString(locations.hashCode()), so the two handlers no longer share a cache entry and each path resolves against its own location set.
Mitigation
Only recent versions of Spring Framework receive free community support and updates. The Spring Framework 6.1.x and 5.3.x lines are past their OSS support window.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring Framework. The OSS fix ships in Spring Framework 7.0.8 (7.0.x line) and 6.2.19 (6.2.x line).
- As an interim hardening step on a vulnerable deployment, avoid registering multiple static-resource handlers that map overlapping request paths to different locations while sharing a single resource cache, or disable the resource chain cache for handlers that mix public and protected locations, so that each lookup resolves against its own location set.
- Leverage a commercial support partner like HeroDevs for post-EOL security support through Never-Ending Support (NES) for Spring Framework.