Skip to content

security: random persisted fallback key for EncryptedStore (not the userData path) - #21

Merged
Baldri merged 1 commit into
mainfrom
claude/session/fix/mingly-encrypted-store-key
Aug 22, 2026
Merged

security: random persisted fallback key for EncryptedStore (not the userData path)#21
Baldri merged 1 commit into
mainfrom
claude/session/fix/mingly-encrypted-store-key

Conversation

@Baldri

@Baldri Baldri commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Was

Der AES-Fallback von EncryptedStore (wenn kein OS-Keychain verfügbar ist) nutzt jetzt einen zufälligen, persistierten Schlüssel statt eines vom userData-Pfad abgeleiteten.

Warum

Der Fallback leitete den Schlüssel aus pbkdf2(userDataPath, fixes-Salt) ab — beide Eingaben sind vorhersehbar. Wer die Store-Datei lesen kann, kann den Schlüssel nachrechnen und entschlüsseln → die „Verschlüsselung" bot keine echte Vertraulichkeit. Audit: Low (crypto).

Änderungen

  • Neu src/main/utils/encrypted-store-crypto.ts (rein, unit-getestet): gcmEncrypt/gcmDecrypt, deriveKeyFromPath (nur noch zum Lesen alter Daten) und getOrCreateRandomKey — persistiert einen zufälligen 32-Byte-Schlüssel mit 0600-Rechten.
  • Fallback-Encryption nutzt den Random-Key und taggt Einträge keyId:'random'; Reads wählen den Schlüssel per keyId (fehlt → Legacy-Pfad-Key). Bestandsdaten entschlüsseln weiter — kein Datenverlust, keine Migration nötig.

Tests

encrypted-store-crypto.test.ts (rot vor Modul): Round-Trip, Falsch-Key-Ablehnung (Auth-Tag), Key-Persistenz, 0600-Rechte, Legacy-Rückwärtskompatibilität. Volle Suite grün, tsc -p tsconfig.main.json clean.

Review-Punkte

  • Der Fallback greift nur ohne OS-Keychain (Edge — nicht auf macOS/Windows) — aber der Schlüssel war das einzige Geheimnis des Stores.
  • Der Random-Key liegt in <store>.key (0600) neben dem Store. Wer Dateizugriff hat, hat beide — aber der Angreifer braucht jetzt Dateizugriff, nicht bloss Kenntnis des Pfads. Für echte Vertraulichkeit ohne Keychain gibt es keine reine Softwarelösung; das ist die pragmatische Härtung.

Dokumentation

Security-Audit (2026-08-21), Mingly Low-Finding (crypto / EncryptedStore).

🤖 Generated with Claude Code

… the userData path

When Electron safeStorage (OS keychain) is unavailable, EncryptedStore fell back
to AES-256-GCM with a key derived from the userData path
(pbkdf2(userDataPath, fixed-salt)). Both inputs are predictable, so anyone able
to read the store file could recompute the key and decrypt it — the encryption
gave no real confidentiality.

- New src/main/utils/encrypted-store-crypto.ts (pure, unit-tested): gcmEncrypt/
  gcmDecrypt, deriveKeyFromPath (kept only to read old data), and
  getOrCreateRandomKey which persists a random 32-byte key with owner-only
  (0600) permissions.
- Fallback encryption now uses the random key and tags entries keyId:'random';
  reads pick the key by keyId (absent → legacy path key), so existing
  path-key data still decrypts — no data loss, no migration needed.

Tests: encrypted-store-crypto.test.ts — round-trip, wrong-key rejection, key
persistence, 0600 perms, legacy backward-compat. Full suite green, tsc clean.
(The fallback only triggers without an OS keychain — an edge case, but the key
was the store's only secret.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Baldri
Baldri merged commit 8404c77 into main Aug 22, 2026
10 checks passed
@Baldri
Baldri deleted the claude/session/fix/mingly-encrypted-store-key branch August 22, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant