Skip to content

Require shared Altcha HMAC key in prod, fail closed on Redis errors#462

Open
simonaszilinskas wants to merge 1 commit into
developfrom
fix/altcha-server-hardening
Open

Require shared Altcha HMAC key in prod, fail closed on Redis errors#462
simonaszilinskas wants to merge 1 commit into
developfrom
fix/altcha-server-hardening

Conversation

@simonaszilinskas

Copy link
Copy Markdown
Collaborator

Why

Two things bothered me while looking at the Altcha code path:

  1. If ALTCHA_HMAC_KEY is unset, each backend process generates its own random key. In any multi-worker / multi-pod setup that means a challenge issued by one process can fail verification on another, and every restart silently invalidates pending challenges. Right now this fails open in a confusing way.
  2. When Redis is unreachable, the replay check is skipped and verification still returns success. So a captured Altcha payload remains replayable for the rest of the challenge expiry window — exactly the protection we lose under the conditions where we'd most want it.

What

  • backend/config.py: refuse to start in non-debug mode when ALTCHA_HMAC_KEY is empty. Dev still gets the auto-generated key, with a warning so it's obvious it's ephemeral.
  • backend/arena/captcha.py: when the Redis call raises, return (False, "Replay check unavailable") instead of allowing the request through. Old fail-open behavior is still available behind a new env flag (ALTCHA_FAIL_OPEN_ON_REDIS_ERROR) for deliberate degraded-mode operation.
  • .env.example: documents both flags.

Test plan

  • Boot with LANGUIA_DEBUG=false ALTCHA_HMAC_KEY="" → raises (verified)
  • Boot with LANGUIA_DEBUG=true ALTCHA_HMAC_KEY="" → ephemeral key + warning (verified)
  • Verify with mocked Redis: error + default → reject; error + opt-in flag → accept; healthy + first use → accept; healthy + replay → reject (all verified)
  • Sanity check on staging that prod-like deploy still boots with the key set

…rors

Two hardening fixes flagged in review:

- ALTCHA_HMAC_KEY: refuse to start in non-debug mode when unset.
  The random fallback breaks across multi-worker / multi-pod setups
  (a challenge issued by one process won't verify on another) and
  silently changes on every restart. Dev still gets the auto-key with
  an explicit warning.

- Replay check: when Redis is unreachable, return False instead of
  silently allowing the request. A captured token would otherwise be
  replayable for the rest of the challenge expiry window. Old
  fail-open behavior is still available behind ALTCHA_FAIL_OPEN_ON_REDIS_ERROR
  for deliberate degraded-mode operation.
@e-lie
e-lie force-pushed the fix/altcha-server-hardening branch from 4d28d01 to f3a4f1c Compare May 1, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant