Summary
The AES-256-GCM encryption key protecting API keys is stored as an exportable JWK in sessionStorage. The device fingerprint for key derivation consists entirely of publicly observable browser properties. If any XSS vulnerability is exploited, all stored API keys can be decrypted.
Risk Assessment
- Risk Level: High
- Likelihood: Conditional (requires XSS)
- Impact: Critical — full API key exfiltration
- Timeline: If XSS is exploited
Affected Code
Key storage (app.js lines 264-266)
const exportedKey = await crypto.subtle.exportKey("jwk", key);
sessionStorage.setItem(ENCRYPTION_KEY_NAME, JSON.stringify(exportedKey));
Predictable fingerprint (app.js lines 276-282)
All 5 fingerprint components (userAgent, language, screen size, timezone, cores) are publicly observable and deterministic.
Attack Chain
- Exploit any XSS vector
JSON.parse(sessionStorage.getItem("ccc_encryption_key")) → JWK
- Import key, decrypt all
ccc_api_key_* from localStorage
- Exfiltrate all API keys
Suggested Fix
Primary: Fix XSS vulnerabilities first
Defense-in-depth: Non-extractable keys
// Change exportable from true to false on line 260
const key = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt, iterations: 100000, hash: "SHA-256" },
keyMaterial,
{ name: "AES-GCM", length: 256 },
false, // non-extractable
["encrypt", "decrypt"],
);
Key would need to be re-derived each session from fingerprint + salt instead of stored.
Summary
The AES-256-GCM encryption key protecting API keys is stored as an exportable JWK in sessionStorage. The device fingerprint for key derivation consists entirely of publicly observable browser properties. If any XSS vulnerability is exploited, all stored API keys can be decrypted.
Risk Assessment
Affected Code
Key storage (
app.jslines 264-266)Predictable fingerprint (
app.jslines 276-282)All 5 fingerprint components (userAgent, language, screen size, timezone, cores) are publicly observable and deterministic.
Attack Chain
JSON.parse(sessionStorage.getItem("ccc_encryption_key"))→ JWKccc_api_key_*from localStorageSuggested Fix
Primary: Fix XSS vulnerabilities first
Defense-in-depth: Non-extractable keys
Key would need to be re-derived each session from fingerprint + salt instead of stored.