CVE-2026-47892

Authorization Bypass
Affects
Spring Framework
in
Spring
No items found.
Versions
>=5.2.5 <=5.2.25, >=5.3.0 <=5.3.49, >=6.0.0 <=6.0.30, >=6.1.0 <=6.1.28, >=6.2.0 <=6.2.19, >=7.0.0 <=7.0.8
Exclamation circle icon
Patch Available

This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.

Overview

Spring Framework is a widely used application framework for the Java platform that provides the core programming and configuration model for modern Java enterprise applications, including the reactive Spring WebFlux web stack. Besides annotated controllers, WebFlux offers a functional programming model (WebFlux.fn) in the spring-webflux module, in which an application composes a RouterFunction from request predicates such as POST("/path"), headers(...), accept(...) and contentType(...) and maps each matching request to a HandlerFunction. A RouterFunction is normally registered as a bean and served through the DispatcherHandler, but the framework also lets an application turn it directly into a standalone HttpHandler or WebHandler with RouterFunctions.toHttpHandler(...) or RouterFunctions.toWebHandler(...), for example to run it on a reactive server without a Spring web application context.

An authorization bypass vulnerability (CVE-2026-47892) has been identified in the WebFlux functional endpoints of spring-webflux, which allows attackers to invoke a route's handler function while bypassing the header-based request predicates that guard it, by sending a crafted CORS pre-flight request. Only applications that serve WebFlux.fn routes standalone through RouterFunctions.toHttpHandler(...) or RouterFunctions.toWebHandler(...), without the DispatcherHandler, are affected. In a DispatcherHandler deployment, which is what a default Spring Boot WebFlux application uses, pre-flight requests never reach a handler function, and Spring MVC functional endpoints (WebMvc.fn) are not affected either.

Under CWE-863, the product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

The advisory rates this vulnerability Medium severity with the CVSS v3.1 vector AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N, which corresponds to a base score of 4.8. The attack vector is Network and no privileges or user interaction are required because the trigger is a single unauthenticated HTTP request, attack complexity is High because the application must use the non-default standalone deployment of WebFlux.fn and rely on a header predicate as a guard, and the Confidentiality and Integrity impacts are Low because the attacker reaches the guarded handler through an OPTIONS request rather than obtaining full control over the application.

This issue affects Spring Framework >=5.2.5 <=5.2.25, >=5.3.0 <=5.3.49, >=6.0.0 <=6.0.30, >=6.1.0 <=6.1.28, >=6.2.0 <=6.2.19, and >=7.0.0 <=7.0.8, including the End-of-Life 5.3.x, 6.1.x, and 6.2.x lines supported by NES for Spring Framework.

Details

Module Info

Vulnerability Info

This Medium-severity vulnerability is found in the spring-webflux package in multiple versions of Spring Framework. The WebFlux.fn request predicates in RequestPredicates are written to support CORS. A CORS pre-flight request is an OPTIONS request that carries both an Origin header and an Access-Control-Request-Method header, as detected by CorsUtils.isPreFlightRequest. For such a request the predicates evaluate the request that the browser would send afterwards rather than the pre-flight itself: the HTTP method predicate substitutes the value of Access-Control-Request-Method for the real method, and every header predicate, which includes headers(...), accept(...) and contentType(...) and any custom predicate built on them, returns true without inspecting the headers at all.

static HttpMethod method(ServerRequest request) {
    if (CorsUtils.isPreFlightRequest(request.exchange().getRequest())) {
        String accessControlRequestMethod =
                request.headers().firstHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
        if (accessControlRequestMethod != null) {
            return HttpMethod.valueOf(accessControlRequestMethod);
        }
    }
    return request.method();
}
private static class HeadersPredicate implements RequestPredicate {

    private final Predicate<ServerRequest.Headers> headersPredicate;

    @Override
    public boolean test(ServerRequest request) {
        if (CorsUtils.isPreFlightRequest(request.exchange().getRequest())) {
            return true;
        }
        else {
            return this.headersPredicate.test(request.headers());
        }
    }
}

This leniency is safe only when routing and handling are separated the way the DispatcherHandler separates them. There, every handler mapping intercepts pre-flight requests before a handler is chosen, so the matched handler function is never invoked for a pre-flight and the CORS processor answers the request instead. The standalone adapter created by RouterFunctions.toHttpHandler(...) and RouterFunctions.toWebHandler(...) has no such step. Its RouterFunctionWebHandler routes the request and then invokes whatever handler function matched, regardless of whether the request is a pre-flight:

private static class RouterFunctionWebHandler implements WebHandler {

    private final HandlerStrategies strategies;

    private final RouterFunction<?> routerFunction;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        return Mono.defer(() -> {
            ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders());
            addAttributes(exchange, request);
            return this.routerFunction.route(request)
                    .switchIfEmpty(createNotFoundError())
                    .flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request)))
                    .flatMap(response -> wrapException(() -> response.writeTo(exchange,
                            new HandlerStrategiesResponseContext(this.strategies))));
        });
    }
}

An attacker therefore sends an OPTIONS request to a guarded path with an Origin header and an Access-Control-Request-Method header naming the method of the target route, for example POST. The method predicate matches because it compares against the requested method, the header predicate matches unconditionally, and RouterFunctionWebHandler executes the handler function with the attacker's request and writes its response back. Any check that the application expressed as a header predicate, such as requiring an API key, a specific Content-Type or an X-Requested-With header, is skipped. Because the request is sent directly rather than by a browser, the same-origin policy offers no protection and no CORS configuration is consulted, unless the application has registered a CorsWebFilter through HandlerStrategies, in which case the filter consumes the pre-flight before it reaches the router.

This vulnerability was introduced in 2020 with Spring Framework 5.2.5.RELEASE.

Steps to Reproduce

1. Add an affected version of spring-webflux to a project, for example 6.2.19, together with spring-test for the mock reactive request and response classes, and run the following. It defines a single route guarded by a header predicate, serves it standalone through RouterFunctions.toHttpHandler, and sends it a pre-flight request that does not carry the required header:

RouterFunction<ServerResponse> route = RouterFunctions.route()
        .POST("/admin/export",
                RequestPredicates.headers(headers -> "s3cr3t".equals(headers.firstHeader("X-Api-Key"))),
                request -> {
                    System.out.println("handler invoked for " + request.method() + " " + request.path());
                    return ServerResponse.ok().bodyValue("export generated");
                })
        .build();

HttpHandler handler = RouterFunctions.toHttpHandler(route);

MockServerHttpRequest request = MockServerHttpRequest.options("https://localhost/admin/export")
        .header(HttpHeaders.ORIGIN, "https://attacker.example")
        .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST")
        .build();

MockServerHttpResponse response = new MockServerHttpResponse();
handler.handle(request, response).block();

System.out.println(response.getStatusCode() + " " + response.getBodyAsString().block());

2. Observe the output handler invoked for OPTIONS /admin/export followed by 200 OK export generated. The handler ran and returned its body even though the request carried no X-Api-Key header and was not even a POST. By contrast, a plain POST to the same path without the header is answered with 404 Not Found because no route matches it.

3. Repeat the same call on a patched version, for example 7.0.9, and observe 403 FORBIDDEN with an empty body: the pre-flight request is rejected by the standalone handler and the handler function is never invoked.

Mitigation

Only recent versions of Spring Framework receive community support. The 5.3.x, 6.1.x, and 6.2.x lines are End-of-Life and will not receive public updates to address this issue, so there is no publicly available fix for those lines other than through a commercial support partner.

Applications that cannot upgrade immediately can remove their exposure by serving their RouterFunction through the DispatcherHandler instead of RouterFunctions.toHttpHandler(...) or RouterFunctions.toWebHandler(...), or by registering a CorsWebFilter through HandlerStrategies.builder().webFilter(...) so that pre-flight requests are answered by the filter before they reach the router. Security decisions should not be expressed as WebFlux.fn header predicates; they belong in a WebFilter, such as the ones provided by Spring Security, which runs before routing for every request including pre-flights.

Users of the affected components should apply one of the following mitigations:

  • Upgrade to a currently supported version of Spring Framework. The open-source fix ships in Spring Framework 7.0.9 on the 7.0.x line.
  • Leverage a commercial support partner like HeroDevs for post-EOL security support, which provides the fix for the 5.3.x, 6.1.x, and 6.2.x lines in nes-v5.3.54, nes-v6.1.30, and nes-v6.2.21.

Credits

  • No public finder credit is listed in the advisory sources checked for this entry.
Vulnerability Details
Severity
Level
CVSS Assessment
Low
>=0 <4
Medium
>=4 <6
High
>=6 <8
Critical
>=8 <10
Medium
ID
CVE-2026-47892
PROJECT Affected
Spring Framework
Versions Affected
>=5.2.5 <=5.2.25, >=5.3.0 <=5.3.49, >=6.0.0 <=6.0.30, >=6.1.0 <=6.1.28, >=6.2.0 <=6.2.19, >=7.0.0 <=7.0.8
NES Versions Affected
Published date
August 29, 2026
≈ Fix date
August 25, 2026
Category
Authorization Bypass
Vex Document
Download VEXHow do I use it?
Sign up for the latest vulnerability alerts fixed in
NES for Spring
Rss feed icon
Subscribe via RSS
or

By submitting the form I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.