CVE-2026-47893

Information Exposure
Affects
Spring Framework
in
Spring
No items found.
Versions
<=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 HTTP endpoints, WebFlux supports reactive WebSocket endpoints in the spring-webflux module: an application maps a URL path to a WebSocketHandler, and the WebSocketHandlerAdapter delegates the HTTP upgrade handshake to a WebSocketService. The default implementation, HandshakeWebSocketService, validates the incoming handshake request before handing it to the server-specific RequestUpgradeStrategy, and rejects requests that do not carry the Upgrade and Connection headers a WebSocket handshake requires.

An information exposure vulnerability (CVE-2026-47893) has been identified in HandshakeWebSocketService in spring-webflux, which allows attackers to indirectly obtain request headers, such as Cookie and Authorization values, through the reason text of the error the handshake raises. When a handshake request arrives with a missing or incorrect Upgrade or Connection header, the service builds the reason of the resulting 400 Bad Request exception by appending the complete set of request headers to it. That reason is then written to the application log at debug level, embedded in the exception message, and, on Spring Framework 6.x and 7.x, copied into the detail field of the exception's problem-detail body, from where error-handling layers can surface it to the client. The weakness is classified as CWE-209, Generation of Error Message Containing Sensitive Information.

MITRE classifies this as CWE-209, where the product generates an error message that includes sensitive information about its environment, users, or associated data.

The advisory rates this vulnerability Low severity with the CVSS v3.1 vector AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N, which corresponds to a base score of 3.1. The attack vector is Network and no privileges are required because any client can send a malformed handshake request to a WebSocket endpoint, attack complexity is High and user interaction is Required because the headers that leak are those of the request that triggers the error, so an attacker has to cause a victim's browser to send a malformed handshake carrying the victim's credentials and then needs an error-handling or logging path that exposes the reason, and the Confidentiality impact is Low because only the headers of that single request are disclosed.

This issue affects Spring Framework <=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 Low-severity vulnerability is found in the spring-webflux package in multiple versions of Spring Framework. HandshakeWebSocketService.handleRequest is invoked for every request that reaches a WebSocket endpoint. After checking that the request is a GET, it verifies that the Upgrade header is websocket and that the Connection header contains upgrade. When either check fails, it calls handleBadRequest with a reason string that is built by concatenating the complete request HttpHeaders object onto a fixed prefix:

HttpHeaders headers = request.getHeaders();

if (!"WebSocket".equalsIgnoreCase(headers.getUpgrade())) {
    return handleBadRequest(exchange, "Invalid 'Upgrade' header: " + headers);
}

List<String> connectionValue = headers.getConnection();
if (!connectionValue.contains("Upgrade") && !connectionValue.contains("upgrade")) {
    return handleBadRequest(exchange, "Invalid 'Connection' header: " + headers);
}

HttpHeaders.toString() renders every header of the request as name:"value" without any redaction, so the reason contains Cookie, Authorization, and any other header the client sent. handleBadRequest then logs the reason and wraps it in the exception that terminates the exchange:

private Mono<Void> handleBadRequest(ServerWebExchange exchange, String reason) {
    if (logger.isDebugEnabled()) {
        logger.debug(exchange.getLogPrefix() + reason);
    }
    return Mono.error(new ServerWebInputException(reason));
}

From there the headers can surface in three places. With debug logging enabled for the class, every malformed handshake writes the full header set to the application log. ServerWebInputException is a ResponseStatusException, whose getMessage() and getReason() include the reason, so any logging or error-reporting layer that records the exception records the headers too. On Spring Framework 6.x and 7.x, ResponseStatusException additionally copies the reason into the detail field of its ProblemDetail, so an application that renders RFC 9457 problem-detail responses for ErrorResponseException, for example through a @ControllerAdvice that extends the WebFlux ResponseEntityExceptionHandler, returns the victim's request headers in the body of the 400 Bad Request response. The framework's default ResponseStatusExceptionHandler only sets the status code, so a plain WebFlux application without such an error-handling layer exposes the headers through logging alone.

The trigger is any GET request to a WebSocket endpoint that lacks a valid Upgrade or Connection header, which an attacker can cause a victim's browser to send, together with the victim's cookies, with an ordinary cross-site request to the endpoint's URL. No authentication is needed to reach the check, and the third handshake check, for the Sec-WebSocket-Key header, uses a constant reason and is not affected.

This vulnerability was introduced in 2017 with Spring Framework 5.0.0.RELEASE.

Steps to Reproduce

1. Add an affected version of spring-webflux to a project, for example 6.2.19, together with spring-context and spring-test for the mock reactive request classes, and run the following. It creates a HandshakeWebSocketService with a no-op upgrade strategy and sends it a GET request that carries a session cookie and a bearer token but no Upgrade header:

HandshakeWebSocketService service = new HandshakeWebSocketService(
        (exchange, handler, subProtocol, factory) -> Mono.empty());

MockServerWebExchange exchange = MockServerWebExchange.from(
        MockServerHttpRequest.get("https://localhost/ws")
                .header("Cookie", "SESSION=victim-session-id")
                .header("Authorization", "Bearer eyJhbGciOi.secret.token")
                .header("Connection", "upgrade"));

try {
    service.handleRequest(exchange, session -> Mono.empty()).block();
}
catch (ResponseStatusException ex) {
    System.out.println(ex.getStatusCode() + " " + ex.getReason());
}

2. Observe the output 400 BAD_REQUEST Invalid 'Upgrade' header: [Cookie:"SESSION=victim-session-id", Authorization:"Bearer eyJhbGciOi.secret.token", Connection:"upgrade"]. The exception reason, and therefore the exception message and the problem-detail detail field, contains the complete set of request headers including the credentials.

3. Repeat the same call on a patched version, for example 7.0.9, and observe 400 BAD_REQUEST Can "Upgrade" only to "WebSocket".: the request is still rejected, but the reason is a constant string that no longer includes any request header.

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 reduce their exposure by keeping debug logging disabled for org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService, and by making sure that their error-handling layer does not return the detail or message of a ResponseStatusException to clients, for example by overriding the handling of ErrorResponseException in a ResponseEntityExceptionHandler subclass to replace the detail with a generic message. These steps limit where the reason text is emitted; they do not stop the headers from being placed in the exception, so upgrading remains the only complete fix.

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
Low
ID
CVE-2026-47893
PROJECT Affected
Spring Framework
Versions Affected
<=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
Information Exposure
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.