Discord bot tokens and OAuth client secrets are stored in plaintext Firestore fields (no app-level encryption) #261

Open
opened 2026-10-05 02:54:08 -04:00 by logan · 0 comments
Owner

Found while building #225 S6 (PR #260). Not fixed there.

What: Radio-bot Discord tokens (bot_tokens/{id}.token) and, from S6 on, each bot's OAuth2 client secret (bot_oauth/{id}.client_secret) are plain strings in Firestore documents. The only protections are:

  • Google's own at-rest encryption, and
  • the Firestore rules that deny client reads.

The codebase has no application-level encryption pattern (no Fernet, KMS or envelope key), so S6 deliberately did not invent one.

Why it matters now:

  • #161: the rules deploy job has failed on every run, so whether the deny rules are live in prod is unverified.
  • If they are not live, any signed-in user could read every bot token. Today that is the whole pool of radio bots, and after S6 the OAuth secrets too.

What to do:

  1. First, the plan's rollout check. With a non-admin ID token, read bot_tokens and bot_oauth, and expect PERMISSION_DENIED.
  2. Then decide whether to encrypt these two fields with a key held outside Firestore (env or Secret Manager). That would be a new pattern: c2-core decrypts on read in discord_rest / discord_oauth / routers/discord.py node_token.

Doesn't taint: nothing measured. This is a secret-storage hardening item, not a behaviour bug.

Found while building #225 S6 (PR #260). Not fixed there. **What:** Radio-bot Discord tokens (`bot_tokens/{id}.token`) and, from S6 on, each bot's OAuth2 client secret (`bot_oauth/{id}.client_secret`) are plain strings in Firestore documents. The only protections are: - Google's own at-rest encryption, and - the Firestore rules that deny client reads. The codebase has no application-level encryption pattern (no Fernet, KMS or envelope key), so S6 deliberately did not invent one. **Why it matters now:** - #161: the rules deploy job has failed on every run, so whether the deny rules are live in prod is unverified. - If they are not live, any signed-in user could read every bot token. Today that is the whole pool of radio bots, and after S6 the OAuth secrets too. **What to do:** 1. First, the plan's rollout check. With a non-admin ID token, read `bot_tokens` and `bot_oauth`, and expect PERMISSION_DENIED. 2. Then decide whether to encrypt these two fields with a key held outside Firestore (env or Secret Manager). That would be a new pattern: c2-core decrypts on read in `discord_rest` / `discord_oauth` / `routers/discord.py` `node_token`. **Doesn't taint:** nothing measured. This is a secret-storage hardening item, not a behaviour bug.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#261