CVE-2026-73180

Authorization Bypass
Affects
Apache Tomcat
in
Apache Tomcat
No items found.
Versions
>=8.5.0 <=8.5.100

Patch Available.

Exclamation circle icon
Patch Available

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

Overview

Apache Tomcat is an open-source web server and servlet container, maintained by the Apache Software Foundation, that implements the Jakarta Servlet, Jakarta Pages (JSP), Jakarta Expression Language, and Jakarta WebSocket specifications and provides a pure-Java HTTP environment for running Java web applications.

An authorization bypass vulnerability (CVE-2026-73180) has been identified in the Apache Tomcat WebSocket server container, which allows an authenticated WebSocket connection to remain open and usable after the HTTP session that authenticated it has been invalidated or has expired. The container recorded each authenticated WebSocket session under the HTTP session identifier captured at handshake time, but closed those sessions by looking them up under the HTTP session's current identifier. Once the identifier was rotated, which Tomcat does by default on authentication, the two keys no longer matched, and ending the HTTP session no longer revoked the WebSocket channel.

Per OWASP: Confirmation of the user's identity, authentication, and session management is critical to protect against authentication-related attacks.

This issue affects multiple versions of Apache Tomcat that ship the WebSocket server container, including every release of the 8.5.x line from 8.5.0 through the final community release 8.5.100.

Module Info

Vulnerability Info

This Low-severity vulnerability is found in the org.apache.tomcat:tomcat-websocket package in multiple versions of Apache Tomcat. The WebSocket specification requires that a WebSocket session opened under an authenticated HTTP session be closed when that HTTP session ends. Tomcat's WebSocket server container implements the requirement with a map of the WebSocket sessions opened under each authenticated HTTP session, keyed by the HTTP session identifier:

private final ConcurrentMap<String, Set<WsSession>> authenticatedSessions = new ConcurrentHashMap<>();

A WebSocket session is entered into that map when it is registered, under the HTTP session identifier the WebSocket session object captured while the upgrade was being processed:

@Override
protected void registerSession(Object key, WsSession wsSession) {
    super.registerSession(key, wsSession);
    if (wsSession.isOpen() && wsSession.getUserPrincipal() != null && wsSession.getHttpSessionId() != null) {
        registerAuthenticatedSession(wsSession, wsSession.getHttpSessionId());
    }
}

WsSession.getHttpSessionId() reads a final field that is assigned once, from the HTTP session in effect at the moment of the handshake, and is never refreshed for the life of the WebSocket connection.

Closure is driven by an HttpSessionListener registered by the container's servlet-container initializer. When the HTTP session is destroyed, that listener asks the container to close the WebSocket sessions associated with the session's current identifier:

@Override
public void sessionDestroyed(HttpSessionEvent se) {
    wsServerContainer.closeAuthenticatedSession(se.getSession().getId());
}

and the container resolves that identifier against the map it populated at handshake time:

public void closeAuthenticatedSession(String httpSessionId) {
    Set<WsSession> wsSessions = authenticatedSessions.remove(httpSessionId);

    if (wsSessions != null && !wsSessions.isEmpty()) {
        for (WsSession wsSession : wsSessions) {
            try {
                wsSession.close(AUTHENTICATED_HTTP_SESSION_CLOSED);
            } catch (IOException e) {
                // Any IOExceptions during close will have been caught and the
                // onError method called.
            }
        }
    }
}

The two identifiers agree only if the HTTP session identifier never changes while the WebSocket connection is open, and that is not something an application can rely on. Tomcat's authenticator rotates the identifier on a successful authentication by default, so a re-login or a step-up authentication on the same session is enough, and an application can rotate it at any time by calling HttpServletRequest.changeSessionId(). After a rotation, the lookup by the current identifier misses the entry stored under the handshake-time identifier, remove returns null, the guard short-circuits, and no WebSocket session is closed. The connection stays open, keeps reporting the original authenticated user through Session.getUserPrincipal(), and continues to serve reads and writes on behalf of an HTTP session that was deliberately terminated by logout or that expired through inactivity.

Exposure is confined to applications that deploy server-side WebSocket endpoints under a security constraint, so that the handshake happens on an authenticated session, and in which the session identifier is rotated after the connection is established and the session is subsequently ended. The party who benefits is the user who was already authenticated, or someone holding that user's live connection, and there is no path for an unauthenticated third party to drive the sequence. The Apache Tomcat security team rates this issue Low and published no CVSS vector for it. Where no rotation occurs, the two identifiers match and the WebSocket session is closed correctly.

Mitigation

Only recent major lines of Apache Tomcat are community-supported. The affected 8.5.x line reached End-of-Life on March 31, 2024 (final community release 8.5.100) and will not receive public updates to address this issue.

There is no configuration-level workaround for this issue. The session tracking at fault is internal to the WebSocket server container and is not governed by any Tomcat setting, so short of running a build that contains the correction, the only alternative is to stop exposing server-side WebSocket endpoints under authentication.

Note that the correction stores a new serializable attribute in the HTTP session, so that ending the session notifies the WebSocket container directly instead of relying on the session identifier. A session written by a corrected node therefore cannot be deserialized by a node that does not have the correction, and the reverse also holds. Every node in a cluster, and any node reading a persisted session store that another node wrote, must be moved to a fixed build at the same time.

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

  • Upgrade to a currently supported Apache Tomcat major line (9.0.x or later) that contains the fix.
  • Leverage a commercial support partner like HeroDevs for post-EOL security support.

Credits

  • 0xCc.zhang (finder)
Vulnerability Details
Severity
Level
CVSS Assessment
Low
>=0 <4
Medium
>=4 <6
High
>=6 <8
Critical
>=8 <10
Low
ID
CVE-2026-73180
PROJECT Affected
Apache Tomcat
Versions Affected
>=8.5.0 <=8.5.100
NES Versions Affected
Published date
August 25, 2026
≈ Fix date
September 18, 2026
Category
Authorization Bypass
Vex Document
Download VEXHow do I use it?
Sign up for the latest vulnerability alerts fixed in
NES for Apache Tomcat
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.