Skip to content

v0.1.2 — fail closed on a corrupt stored DBSC record

Choose a tag to compare

@ScottHelme ScottHelme released this 16 May 17:13
921d3ca

Security / correctness

Fail closed on a corrupt stored DBSC record.

A stored binding (or pending-registration) record that was present but unparseable previously decoded to null, and the recommended host gate treats null as "no binding → degrade to cookie auth". A corrupt binding therefore silently downgraded a hard-DBSC session to plain cookie auth, re-opening the stolen-cookie hole DBSC exists to close. The docblocks claimed this "failed closed"; it did not.

Not client-triggerable. The write path always emits valid, well-typed JSON; the only client-derived field (publicKeyPem) is structurally validated first. The realistic trigger is an internal phpredis-serializer / truncation / cross-version-schema event. This is a defense-in-depth and contract-correctness fix.

Changes

  • New Exception\CorruptStateException (extends DbscException).
  • Binding::fromJson() / PendingRegistration::fromJson() now throw CorruptStateException instead of returning null; return type ?selfself. null is reserved for "no record".
  • StoreInterface documents the absent-vs-corrupt contract explicitly.
  • DbscServer::revoke() swallows CorruptStateException so a corrupt record is still deleted and audited rather than aborting teardown; @throws CorruptStateException added to the store-reading public methods.

Upgrading

Source/binary compatible for the recommended host wiring: CorruptStateException extends DbscException, so the gate read fails closed (uncaught → 500), the refresh terminal path's existing catch (… | DbscException) revokes + logs out, and register → 401. InMemoryStore is unchanged. If you call Binding::fromJson() / PendingRegistration::fromJson() directly, note they no longer return null — wrap in try/catch (CorruptStateException).

Full Changelog: v0.1.1...v0.1.2