destroyAll() zeroes session keys that hooks.server.ts hands out by reference #141
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lz/agent-nexus#141
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
SessionStore.destroyAll()zeroes every stored session key in place, but thosesame buffers are published to live request scopes by reference. A request already in
flight when a passphrase rotation lands has its
masterKeyzeroed underneath it, andwill seal its next secret under 32 zero bytes — writing a row that nothing can ever
open again.
Silent, and it corrupts data rather than failing.
The two halves
nexus/src/lib/server/auth/session.ts:92—destroyAll()doess.key.fill(0)onevery stored session key.
nexus/src/hooks.server.ts:75—event.locals.masterKey = session.key, no copy.Every route handler that seals a value (
connections,repo_env_vars) holds thatexact buffer for the life of the request.
nexus/src/routes/api/auth/rotate-passphrase/+server.ts:43is the only caller, so thewindow is one rotation, but any concurrent request sealing a secret inside it writes an
unopenable row.
Not the same as the
result.newKey.fill(0)next to itWorth stating, because the two look identical and only one is a bug. Rotation's own
fill(0)(rotate-passphrase/+server.ts:45) is safe:unlockMasterPassphrasealways allocates fresh (
open()returns aBuffer.concat,newDek()returnsrandomBytes), and the route copies withBuffer.frombefore storing, so it can onlyreach memory nothing else holds. Verified by probe during the review of
b6daea5.destroyAll()is the inverse: the store zeroes memory it has already published. Samemechanism, opposite ownership.
Envelope encryption makes the fix nearly free
Before envelope encryption, zeroing other sessions' keys had a point — rotation changed
the key the data was sealed under, so those copies were both stale and dangerous.
After it (PR for the vault envelope work), the DEK does not change across a rotation.
Zeroing the other sessions' copies buys nothing: the identical bytes stay resident in the
caller's newly created session either way.
destroyAll()can simply drop the map entriesand let GC take the buffers.
If the in-place wipe is kept for hygiene, then
hooks.server.ts:75must hand out a copyinstead — but that allocates on every authenticated request to defend against a single
rare operator action, which is the worse trade.
Reproduction sketch
locals.masterKey.Provenance
Found during the code-quality review of the vault envelope-encryption work
(
b6daea5). Both halves pre-date that commit and nothing in it introduces orwidens the problem — the diff does not touch either file. Filed separately rather than
fixed there to keep that change to its reviewed scope.