v0.339.0 rejects cross-origin WebSocket connections

Strawberry now checks the Origin header of WebSocket handshakes and refuses connections from browser pages on other origins with an HTTP 403 response. Previously any website could open a WebSocket to your GraphQL endpoint, and the browser would send your user’s cookies with it. Applications that authenticate WebSockets from cookies could therefore have operations run as their users by another website (cross-site WebSocket hijacking).

Same-origin pages and clients that don’t send an Origin header, such as mobile apps, server-side clients and tests, are not affected.

Who needs to change something

If your frontend is served from a different origin than your GraphQL endpoint, for example https://app.example.com talking to https://api.example.com , or a development server on http://localhost:5173 talking to http://localhost:8000 , its WebSocket connections are now refused. This includes development servers that proxy WebSockets to Strawberry while rewriting the Host header, such as Vite’s server.proxy with changeOrigin: true . Strawberry logs a warning on the strawberry.ws logger naming the rejected origin.

List the origins that should be able to connect in allowed_origins :

from strawberry.fastapi import GraphQLRouter
graphql_app = GraphQLRouter(
schema,
allowed_origins=["https://app.example.com", "http://localhost:5173"],
)

The option is available on every integration that supports WebSockets: ASGI, FastAPI, AIOHTTP, Quart, Litestar (make_graphql_controller ) and Channels (GraphQLWSConsumer ). For rules a list can’t express, override is_websocket_origin_allowed . See Restricting WebSocket origins for details.

To restore the previous behaviour, pass allowed_origins=["*"] . Only do this if your WebSocket endpoint doesn’t rely on cookies or other credentials that browsers send automatically.

Custom integrations

Views built directly on AsyncBaseHTTPView must implement two new methods: get_websocket_headers , which returns the handshake headers, and reject_websocket , which refuses the handshake without accepting the connection. Views that don’t support WebSockets can raise NotImplementedError from both.

Contributors:

Edit this page on GitHub