Missing Origin validation on WebSocket upgrades allows Cross-Site WebSocket Hijacking
Summary
The WebSocket upgrade handler accepted cross-origin upgrade requests that carried the directus_session_token session
cookie, without validating the Origin header of the handshake. A browser automatically attaches cookies to a WebSocket
handshake and, unlike the REST and GraphQL transports, the upgrade path is registered directly on the HTTP server and
therefore never passes through the CORS middleware. In affected deployments, a page on an attacker-controlled origin
could open a WebSocket to a Directus instance and have the connection authenticated as the visiting user.
Details
When a WebSocket upgrade request arrived with a session cookie present, the connection was authenticated from that
cookie and the socket was established with the corresponding user's accountability. No comparison was made against the
request host, PUBLIC_URL, or the CORS_ORIGIN allowlist before accepting the upgrade.
The cookie branch was evaluated ahead of the configured WebSocket authentication mode, so setting WEBSOCKETS_REST_AUTH
or WEBSOCKETS_GRAPHQL_AUTH to handshake or strict did not prevent a cookie-bearing handshake from being accepted.
Exploitation requires all of the following:
- WebSockets are enabled (
WEBSOCKETS_ENABLED=true). This is not the default.
- The victim is signed in using session cookie authentication, which is the mode the Admin App uses.
- The victim's browser attaches the session cookie to the attacker's cross-origin handshake. This happens when
SESSION_COOKIE_SAME_SITE is set to none (a configuration used for cross-origin Admin App deployments), or when the
attacker controls a site that is same-site with the API host, such as a sibling subdomain, in which case the default
lax setting still sends the cookie.
- The victim visits the attacker-controlled page while their session is valid.
Impact
An attacker who can get a signed-in user to load a page under their control can drive an authenticated WebSocket session
with that user's permissions. Over that connection they can read, create, update, and delete items, and open
subscriptions, on any collection the victim has access to. If the victim is an administrator, this extends to system
collections such as directus_users, directus_roles, and directus_permissions.
The attacker's page can read WebSocket responses directly, so this results in data disclosure as well as unauthorized
writes. The session token itself is HttpOnly and is not exposed to the attacker's script.
Workarounds
For deployments that cannot upgrade immediately:
- Disable WebSockets (
WEBSOCKETS_ENABLED=false) if they are not in use.
- Avoid
SESSION_COOKIE_SAME_SITE=none. Serving the Admin App and the API from the same site allows the default lax
value, which prevents the cross-site case.
- Enforce an
Origin allowlist for the WebSocket paths at a reverse proxy in front of Directus.
- Treat control of any subdomain sharing the API's registrable domain as equivalent to control of the session, and
review subdomain ownership accordingly.
Credit
Reported by @tonghuaroot
Missing Origin validation on WebSocket upgrades allows Cross-Site WebSocket Hijacking
Summary
The WebSocket upgrade handler accepted cross-origin upgrade requests that carried the
directus_session_tokensessioncookie, without validating the
Originheader of the handshake. A browser automatically attaches cookies to a WebSockethandshake and, unlike the REST and GraphQL transports, the upgrade path is registered directly on the HTTP server and
therefore never passes through the CORS middleware. In affected deployments, a page on an attacker-controlled origin
could open a WebSocket to a Directus instance and have the connection authenticated as the visiting user.
Details
When a WebSocket upgrade request arrived with a session cookie present, the connection was authenticated from that
cookie and the socket was established with the corresponding user's accountability. No comparison was made against the
request host,
PUBLIC_URL, or theCORS_ORIGINallowlist before accepting the upgrade.The cookie branch was evaluated ahead of the configured WebSocket authentication mode, so setting
WEBSOCKETS_REST_AUTHor
WEBSOCKETS_GRAPHQL_AUTHtohandshakeorstrictdid not prevent a cookie-bearing handshake from being accepted.Exploitation requires all of the following:
WEBSOCKETS_ENABLED=true). This is not the default.SESSION_COOKIE_SAME_SITEis set tonone(a configuration used for cross-origin Admin App deployments), or when theattacker controls a site that is same-site with the API host, such as a sibling subdomain, in which case the default
laxsetting still sends the cookie.Impact
An attacker who can get a signed-in user to load a page under their control can drive an authenticated WebSocket session
with that user's permissions. Over that connection they can read, create, update, and delete items, and open
subscriptions, on any collection the victim has access to. If the victim is an administrator, this extends to system
collections such as
directus_users,directus_roles, anddirectus_permissions.The attacker's page can read WebSocket responses directly, so this results in data disclosure as well as unauthorized
writes. The session token itself is
HttpOnlyand is not exposed to the attacker's script.Workarounds
For deployments that cannot upgrade immediately:
WEBSOCKETS_ENABLED=false) if they are not in use.SESSION_COOKIE_SAME_SITE=none. Serving the Admin App and the API from the same site allows the defaultlaxvalue, which prevents the cross-site case.
Originallowlist for the WebSocket paths at a reverse proxy in front of Directus.review subdomain ownership accordingly.
Credit
Reported by @tonghuaroot