fix: encrypt Immich API key at rest using AES-256-GCM

Per-user Immich API keys were stored as plaintext in the users table,
giving any attacker with DB read access full control over each user's
Immich photo server. Keys are now encrypted on write with
maybe_encrypt_api_key() and decrypted at the point of use via a shared
getImmichCredentials() helper. A new migration (index 66) back-fills
encryption for any existing plaintext values on startup.
This commit is contained in:
jubnl
2026-04-01 05:50:28 +02:00
parent 78695b4e03
commit b515880adb
2 changed files with 44 additions and 28 deletions
+9
View File
@@ -463,6 +463,15 @@ function runMigrations(db: Database.Database): void {
db.prepare("UPDATE app_settings SET value = ? WHERE key = 'smtp_pass'").run(encrypt_api_key(row.value));
}
},
// Encrypt any plaintext immich_api_key values in the users table
() => {
const rows = db.prepare(
"SELECT id, immich_api_key FROM users WHERE immich_api_key IS NOT NULL AND immich_api_key != '' AND immich_api_key NOT LIKE 'enc:v1:%'"
).all() as { id: number; immich_api_key: string }[];
for (const row of rows) {
db.prepare('UPDATE users SET immich_api_key = ? WHERE id = ?').run(encrypt_api_key(row.immich_api_key), row.id);
}
},
];
if (currentVersion < migrations.length) {