GHSA-xx4j-w367-7247

    Dashboard / Vulnerabilities / GHSA-xx4j-w367-7247

    GHSA-xx4j-w367-7247

    Published: 25 Aug 2026Last Modified: 26 Aug 2026

    Summary: djust authentication bypass: a login_required / on_mount LiveView mount redirect does not close the WebSocket, allowing an unauthenticated client to dispatch event-handler calls

    Details: ### Impact djust's `LiveViewConsumer` mounts a `LiveView` over a WebSocket. When a view is gated (`login_required` / `permission_required`, or an `on_mount` hook that returns a redirect) and the connecting user is not authorized, the consumer sent the client a `{"type":"navigate","to":...}` redirect frame and then `return`ed — **without closing the socket and without clearing `self.view_instance`**. Only the `PermissionDenied` branch closed the connection (`close(4403)`). A real browser obeys the navigate frame and leaves, hiding the problem. A **raw WebSocket client that ignores the redirect** keeps an open, mounted socket. Because `handle_event` did not re-check authentication/authorization after mount, that client could then send `{"type":"event", ...}` frames and invoke any `@event_handler` method on the gated view **with no authenticated session** — an authentication bypass on the live mutation path. **Who is affected:** apps that expose `LiveView`s gated by `login_required` / `permission_required` / a redirecting `on_mount` hook, where the gated view's event handlers perform sensitive reads or mutations and do not independently re-verify the user. Exploitation requires a non-browser WebSocket client and knowledge (or enumeration) of the view path and event names. ### Patches Fixed in **djust 1.0.4** (commit `1ae8aa9`, PR #1780). Both the auth-redirect and `on_mount`-hook-redirect branches of `handle_mount` now send the navigate frame **and then `close(code=4403)` and clear `self.view_instance`**, mirroring the existing `PermissionDenied` branch. Public / authorized mounts are unchanged. The same path is reachable via `handle_live_redirect_mount` (which delegates to `handle_mount`) and is covered by the same fix. 1.0.4 also adds an opt-in defense-in-depth control, `LIVEVIEW_CONFIG['reauth_on_event'] = True` (default OFF), which re-resolves the user from the session and re-runs the view's auth check on **every** event for gated views. ### Workarounds Upgrade to 1.0.4. If you cannot upgrade immediately, on affected versions ensure that **every `@event_handler` on a gated `LiveView` independently verifies the request user is authenticated and authorized** (e.g. check `request.user.is_authenticated` / permissions at the top of each handler), since the framework does not re-check after mount on `< 1.0.4`. Alternatively, override the consumer's `handle_mount` to `await self.close(code=4403)` after emitting an auth redirect. ### Proof of concept Using Channels' `WebsocketCommunicator` against `LiveViewConsumer.as_asgi()` with an **anonymous** scope, mount a `login_required` view: the server emits a `navigate` frame but the socket stays open. Sending a subsequent `{"type":"event", "handler":"<mutating_handler>", ...}` frame reaches the handler and executes it without an authenticated session. On 1.0.4 the socket is closed with code `4403` immediately after the redirect and the event frame is rejected. (Regression test: `tests/test_ws_auth_close_socket.py`.) ### Credits Discovered internally during the djust v1.1.0 WebSocket-auth security review.

    Affected packages

    Package

    Name: djust

    Purl: pkg:pypi/djust

    Affected ranges

    Type: ECOSYSTEM

    Events:

    Introduced- 0
    Fixed -1.0.4

    Affected versions

    0.1.0
    0.1.2
    0.1.3
    0.1.4
    0.1.5
    0.1.6
    0.1.7
    0.1.8
    0.1.9

    Common Vulnerability Scoring System

    Attack Vector
    Network
    Adjacent
    Local
    Physical
    Privileges Required
    None
    Low
    High
    User Interaction
    None
    Required
    Scope
    Unchanged
    Changed
    Confidentiality
    None
    Low
    High
    Integrity
    None
    Low
    High
    Availability
    None
    Low
    High