You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(security): encrypt userId and userHash at rest with AES-256-GCM
Store game credentials encrypted in SQLite so a database leak cannot be
directly exploited. A stolen DB file yields only opaque ciphertext.
Implementation
- src/bot/utils/crypto.ts (new): AES-256-GCM helpers using Node.js crypto.
Key loaded from ENCRYPTION_KEY env var (64-char hex / 32 bytes).
Ciphertext format: enc1:<iv_hex>:<authTag_hex>:<ciphertext_hex>.
Key is validated eagerly at module load time (fail-fast).
- src/bot/database/userManager.ts: encrypt on write, decrypt on read.
saveCredentials() rejects empty userId/userHash.
migratePlaintextCredentials() transparently upgrades existing rows on
startup; each field is migrated independently (handles mixed-state rows).
- src/bot/bot.ts: calls migratePlaintextCredentials() after DB init.
- src/test/setup.ts: sets ENCRYPTION_KEY to a clearly non-secret
'deadbeef'-repeated test fixture to avoid secret-scanner false positives.
- src/bot/database/userManager.test.ts: 5 new migration tests; existing
tests updated to assert encrypted-at-rest storage.
Configuration
- .env.example: documents ENCRYPTION_KEY with generation hint.
- docker-compose.yml / docker-compose.example.yml: forwards ENCRYPTION_KEY
into the container.
Docs updated in README, docs/development.md, docs/full-documentation.md,
docs/podman.md, docs/github-secrets.md: ENCRYPTION_KEY listed as a required
variable with openssl rand -hex 32 generation hint and a warning that the
key must be generated once and retained — changing it after credentials have
been saved makes those rows unreadable.
Signed-off-by: Michael Cramer <michael@bigmichi1.de>
0 commit comments