Skip to content

qt/security: QtWebSocketBackend's three async control paths never stamp env.session, so shared register/attach/assign reach the server unauthenticated #495

Description

@Yaraslaut

Summary

QtWebSocketBackend::registerModelSharedAsync, attachModelAsync and assignPrimaryAsync build their control envelopes and encode them without env.session = _session;. Their synchronous counterparts all stamp it. Since the async path is the WASM path, a WASM client's shared registration, attach and primary assignment reach an authenticating server carrying a default-constructed session.

Verification status

Verified by reading both sides of each pair. Not exercised against a live authorizing server — no end-to-end reproduction, and I have not measured what a given IAuthorizer does with an empty context. Revision: origin/master adfe8e5f plus this branch's doc-only commits; src/qt/qt_websocket_backend.cpp.

grep -n "env.session = _session" src/qt/qt_websocket_backend.cpp → lines 143, 196, 329, 344, 354, 386, 419.

Mapping those against the function boundaries:

Method Lines Builds Stamps session?
sendRegisterAsync (used by registerModelAsync) 190-211 makeRegister yes (:196)
registerModelShared 321-333 makeRegisterShared yes (:329)
attachModel 333-346 makeAttach yes (:344)
assignPrimary 348-356 makeAssign yes (:354)
registerModelSharedAsync 223-253 makeRegisterShared no
attachModelAsync 256-289 makeAttach no
assignPrimaryAsync 358-382 makeAssign no

Each async body reads, in full: build env → set env.callIdwire::encode(env)sendTextMessage. There is no session assignment anywhere between.

include/morph/net/socket_backend.hpp stamps every equivalent site (:143, 174, 196, 210, 222, 260), so this is a Qt-side omission rather than a deliberate protocol choice.

Why it matters

RemoteServer authenticates and authorizes from env.session:

  • remote.hpp:617-620stampVerifiedPrincipal
  • remote.hpp:1068 (register), :1157 (attach), :1232 (assign)

So on the async path those three verbs are evaluated against an empty session — an unauthenticated principal — while the identical synchronous verbs are not.

It also contradicts the documented contract in two places:

  • qt_websocket_backend.hpp:412-416: "Installs the session stamped onto every control envelope this backend subsequently builds (register, registerShared, attach, assign, deregister)."
  • docs/spec/core/backend.md:85 and :131-148 state it as a cross-backend rule: "Every wire-backed implementation stamps the session … onto these."

Scope note

assignPrimaryAsync additionally has no if (!_cfg.asyncRegistrationEnabled) return false; gate, unlike the other three async hooks (:164, :228, :262). An embedder that opted out of async registration still gets asynchronous assign. Undocumented; mentioning it because a fix here touches the same function.

What would change the verdict

  • Close it if the omission is deliberate and the server is shown to re-derive the principal for these three verbs from connection state rather than the envelope — in which case the three doc sites above need correcting instead, and the synchronous stamps are redundant.
  • Raise it by pointing a SigningAuthorizer-configured RemoteServer at a WASM/async client and showing a shared attach succeeding that the synchronous path would refuse. That is the test this needs and does not have.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: qtSubsystem: qtbugSomething isn't workingtriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions