feat(security): encrypt profile passwords at rest with AES-GCM
Passwords are now encrypted with Web Crypto AES-GCM (256-bit) before being stored in browser.storage.local. The random key is persisted so it survives browser/service-worker restarts (otherwise old ciphertext could never be decrypted). Encryption/decryption happen in the background via encrypt-password/decrypt-password messages; autoLogin decrypts on the fly and falls back to a legacy plaintext password for pre-encryption profiles. The popup never reveals a stored password and preserves it when editing with a blank field. Removes the plaintext-password-at-rest smell flagged in the design review. Note: this is at-rest obfuscation-level security (the key ships with the extension); the extension only talks to the school portal.
This commit is contained in:
+14
-1
@@ -39,7 +39,20 @@
|
||||
const passField = document.querySelector('input[type="password"]');
|
||||
if (!userField || !passField) return;
|
||||
userField.value = active.username;
|
||||
passField.value = active.password;
|
||||
// Password is encrypted at rest; decrypt via the background. Legacy profiles
|
||||
// saved before encryption keep a plaintext `password` field as a fallback.
|
||||
let password = '';
|
||||
if (active.passwordEnc) {
|
||||
try {
|
||||
const res = await browser.runtime.sendMessage({ type: 'decrypt-password', cipher: active.passwordEnc });
|
||||
if (res && res.ok) password = res.password;
|
||||
} catch (e) {
|
||||
password = '';
|
||||
}
|
||||
} else {
|
||||
password = active.password || '';
|
||||
}
|
||||
passField.value = password;
|
||||
userField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
passField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
const form = userField.closest('form') || passField.closest('form');
|
||||
|
||||
Reference in New Issue
Block a user