Severity: Critical
Area: frontend/auth
Problem: Long-lived API keys were stored in sessionStorage, making them vulnerable to XSS attacks.
- File:
contexts/auth-context.tsx - Removed
apiKeyfrom AuthState interface - Removed
API_KEY_KEYconstant - Removed all
sessionStorage.setItem(API_KEY_KEY, ...)calls - Updated
login()function signature to not require API key - Authentication now relies on httpOnly cookies set by backend
- File:
lib/api/client.ts - Removed
setToken()andgetToken()functions - Removed Bearer token and x-api-key headers from requests
- Authentication now handled via httpOnly cookies (credentials: 'include')
- Updated documentation to reflect cookie-based auth
-
File:
app/auth/signin/page.tsx- Removed API key parameter from
login()call - Moved passcode to secure in-memory storage instead of sessionStorage
- Removed API key parameter from
-
File:
app/auth/2fa/page.tsx- Removed API key parameter from
login()call
- Removed API key parameter from
-
File:
lib/passcode-manager.ts(NEW)- Created in-memory passcode storage
- Passcode is NOT stored in sessionStorage (XSS-safe)
- Cleared on logout or page refresh
- Only used for local wallet decryption
-
File:
lib/wallet-storage.ts- Updated to use in-memory passcode manager
- Removed sessionStorage passcode retrieval
- File:
middleware.ts(NEW)- Added CSP headers to prevent XSS attacks
- Added X-Frame-Options, X-Content-Type-Options
- Added Referrer-Policy and Permissions-Policy
- Restricts script sources and inline scripts
- ❌ API keys stored in sessionStorage (JavaScript-accessible)
- ❌ Passcode stored in sessionStorage
- ❌ No Content Security Policy
- ❌ Vulnerable to XSS token exfiltration
- ❌ Long-lived tokens with no expiration
- ✅ API keys in httpOnly cookies (JavaScript-inaccessible)
- ✅ Passcode in memory only (cleared on refresh/logout)
- ✅ Content Security Policy headers active
- ✅ XSS attacks cannot steal authentication tokens
- ✅ Backend controls token lifecycle
✅ No sessionStorage.getItem('acbu_api_key') in code
✅ No sessionStorage.setItem('acbu_api_key', ...) in code
✅ No sessionStorage.setItem('acbu_passcode', ...) in code
✅ Authentication works via httpOnly cookies
✅ CSP headers block inline scripts
✅ Passcode stored in memory only (for wallet operations)
The backend must:
- Set httpOnly session cookie on successful login
- Cookie flags:
httpOnly=true,secure=true,sameSite=strict - Validate cookie on all authenticated requests
- Clear cookie on logout
Based on code comments, the backend already supports this.
- Login flow works without API key in response
- Authenticated API requests succeed (cookies sent automatically)
- Logout clears session properly
- 2FA flow works correctly
- Wallet operations work with in-memory passcode
- Page refresh requires re-authentication
- CSP headers present in browser DevTools
- No API keys visible in sessionStorage (DevTools > Application > Session Storage)
-
Passcode in Memory: Passcode is stored in memory for wallet operations. It's cleared on page refresh, requiring users to log in again for wallet operations. This is more secure than sessionStorage but less convenient.
-
Temporary Passphrase: The
temp_passphrasefor new wallet setup is still in sessionStorage temporarily. This is short-lived and only for the wallet setup flow. -
2FA Challenge Token: Still in sessionStorage but this is acceptable as it's:
- Short-lived (expires quickly)
- Single-use token
- Not a long-lived credential
- Frontend changes are backward compatible if backend still returns
api_key(it's just ignored) - No database migrations required
- Users will need to log in again after deployment
- Existing sessions in sessionStorage will be invalid
- Implement short-lived JWT access tokens + refresh tokens
- Add token rotation on refresh
- Implement "remember me" functionality with secure refresh tokens
- Add rate limiting on authentication endpoints
- Consider hardware security key support (WebAuthn)