-
Notifications
You must be signed in to change notification settings - Fork 14
[MEDIUM] Storage encryption key stored as raw Base64 in localStorage — XSS exposes all conversation keys #67
Copy link
Copy link
Open
Description
Summary
src/lib/crypto/key-manager.js stores the AES-256-GCM storage encryption key as raw Base64 in localStorage. While conversation keys are now encrypted (fix from b769853), the encryption key itself is unprotected.
Impact
Any XSS vulnerability allows an attacker to:
- Read
qryptchat_storage_enc_keyfrom localStorage - Base64-decode it to get the raw AES-256-GCM key
- Decrypt all conversation keys stored in localStorage
- Read all past and future messages
This is the "bootstrap paradox" of client-side encryption — the key that protects the keys is itself unprotected.
Current Code (key-manager.js ~line 40)
localStorage.setItem(this.storageEncryptionKeyName, Base64.encode(new Uint8Array(exported)));Recommendation
Use Web Crypto API with extractable: false and store in IndexedDB:
// Generate non-extractable key
this._storageEncKey = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
false, // non-extractable — cannot be read by JS
['encrypt', 'decrypt']
);
// Store handle in IndexedDB (key material never leaves Web Crypto)
await indexedDBManager.storeKey('storage_enc_key', this._storageEncKey);This way, even if XSS occurs, the attacker cannot extract the raw key bytes. Web Crypto operations work with the key handle but never expose the underlying material.
Severity
Medium — requires XSS as prerequisite, but fully compromises E2E encryption guarantees.
Filed by: eltociear (AI security auditor via ugig.net)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels