Session Secret Lifecycle Security
Verifies that the wallet's session controller zeroises unlocked secrets on lock and never keeps mnemonics or seeds in memory longer than necessary.
Overview
This suite focuses on in-memory handling of extremely sensitive material: unlocked seed hex and mnemonic phrases. It asserts that:
- Unlocked secrets are available while a session is active and the wallet is unlocked.
- Calling
lockSessionclears unlocked secrets and marks the session as locked. - Subsequent exports expose
nullforunlockedSeedandmnemonic.
This behaviour follows guidance from mobile and client security standards on keeping sensitive data out of long-lived memory and reduces the blast radius of any transient compromise.
Running the Tests
# From keythings-monorepo root
# Run as part of the comprehensive security suite:
bun run security:comprehensive
# Or run this suite directly:
bun tests/security/suites/session-secret-lifecycle-tests.tsLock Zeroisation Test
The core test exercises the full lifecycle: save a session with a seed and mnemonic, verify they are available while unlocked, then lock and confirm secrets are cleared.
async function runLockZeroisationTest(): Promise<void> {
const { sessionController } = await import('../../../src/background/session-controller')
await sessionController.saveSession(
'keeta_address',
'test',
'deadbeef',
'test mnemonic',
'wallet-id',
)
const unlocked = await sessionController.getState()
assert.equal(unlocked.session?.isLocked, false)
const before = await sessionController.exportSecretsForSender({
id: chrome.runtime.id,
url: `chrome-extension://${chrome.runtime.id}/popup.html`,
} as chrome.runtime.MessageSender)
assert(before?.secrets.unlockedSeed, 'Unlocked seed should be present before lock')
await sessionController.lockSession()
const after = await sessionController.exportSecretsForSender({
id: chrome.runtime.id,
url: `chrome-extension://${chrome.runtime.id}/popup.html`,
} as chrome.runtime.MessageSender)
if (after?.secrets.unlockedSeed === null && after.secrets.mnemonic === null) {
recordFinding({
area: 'Session',
name: 'Unlocked seed and mnemonic cleared on lock',
severity: 'low',
})
} else {
recordFinding({
area: 'Session',
name: 'Secrets still present after lock',
severity: 'high',
})
}
}Vulnerability detected: If unlockedSeed or mnemonic remain non-null after lockSession, the wallet would keep live signing material in memory beyond the expected lifetime of the session.
Severity: Secrets persisting after lock are treated as high severity. A clean zeroisation path is recorded as low to capture positive evidence.
Vulnerability Classes & Severity
| Check | Vulnerability | Severity |
|---|---|---|
| Lock zeroisation | Unlocked seed or mnemonic remains present after session lock | High |
Research & References
The design of this suite is influenced by secure mobile and client storage guidance:
- Memory for Sensitive Data: The OWASP Mobile Application Security Testing Guide describes testing and clearing sensitive data from process memory in MASTG-TEST-0011: Testing Memory for Sensitive Data, which recommends minimising lifetime and ensuring predictable clearing of keys.
- Sensitive Data Exposure: OWASP Top 10 categories for sensitive data exposure emphasise that cryptographic keys and secrets must not be left in memory longer than strictly necessary.
Combined with the Data Exfiltration suite, these tests provide end-to-end assurance that secrets are both cleared from memory and never transmitted over the network or to logs.