Files
sub2api/backend
Nick 89ba561a6f fix(backup): keep the S3 secret encrypted when a save inherits it
UpdateS3Config only encrypted the SecretAccessKey on the branch where the
caller supplied a new one. The other branch inherits the stored secret
through loadS3Config, which *decrypts* it — so that path wrote the
plaintext straight back to the settings row and overwrote the ciphertext.

It is the normal path, not an edge case. UpdateS3Config clears the secret
in its own return value, so the admin form's field is empty afterwards.
Any later save — changing the endpoint, saving the schedule, pressing save
twice — sends an empty secret and takes the inheriting branch. One extra
save is enough to leave the secret in plaintext at rest.

Nothing surfaced because loadS3Config falls back to the raw value when
decryption fails, for compatibility with pre-encryption rows. Backups kept
working and the only trace was a line per read:

  [Backup] S3 SecretAccessKey 解密失败(可能是旧的未加密数据):
  decrypt: cipher: message authentication failed

Seen on a live install: the stored value was 64 characters, exactly the
plaintext length of an R2 secret, where AES-GCM + base64 would have been
124. The compatibility fallback turns a one-time write bug into a
permanent plaintext secret.

It also feeds itself: pg_dump includes the settings table, so the backup
uploaded to the bucket carries the credentials for that same bucket.

Fix: hoist the encryption out of the if/else so it runs for a non-empty
secret whichever way it arrived. The ephemeral-key guard (#4524) moves
with it, and the no-secret-at-all case still skips encryption entirely, so
an update with no stored secret keeps working under an auto-generated key.

The existing KeepExistingSecret test passed either way, because it only
asserted loadS3Config returns the plaintext — true with the fallback too.
The new test asserts on the row itself: still ciphertext after a second
save, not double-wrapped after a third.

Checked the new test fails without the fix and passes with it.
2026-07-25 19:57:51 +07:00
..