Enforce Idempotency-Key on ingress to block naive replays #7

Open
opened 2026-05-11 13:10:10 +00:00 by hugooconnor · 0 comments
hugooconnor commented 2026-05-11 13:10:10 +00:00 (Migrated from codeberg.org)

The ingress handler currently reads the Idempotency-Key header and passes it through to the dispatcher (cbcl-ingress-handler.lfe:39), but the dispatcher doesn't use it for replay protection — every POST mints a fresh receipt regardless of whether the same key has been seen recently. An attacker who captures a (lang elf (ask @router "do-work" …)) from a network trace can re-POST it and the work re-dispatches.

Mitigation

Treat Idempotency-Key as authoritative when present:

  • If (principal, key) has been submitted in the last N minutes (window TBD; suggest 1h), return the prior {receipt_id, seq, capability, dispatched_to} instead of dispatching again.
  • If absent, behave as today (mint fresh receipt) — keeps the path open for clients that don't care.

Storage can be a small in-memory ETS table with TTL eviction (or piggyback on cbcl-storage). Keyed by (principal, key) so different principals using the same string don't collide.

Acceptance

  • cbcl-ingress-handler (or a new cbcl-idempotency-store gen_server) caches (principal, key) → ReceiptResponse with TTL.
  • On repeat: return cached response, do not re-call cbcl-dispatcher:submit.
  • Absent Idempotency-Key: behaviour unchanged.
  • TTL configurable; default 3600 seconds.
  • Tests:
    • First POST returns fresh receipt.
    • Same (principal, key) within TTL returns identical receipt and does not re-dispatch.
    • Different principal, same key: independent receipts.
    • Same key, different body: still returns cached response (key is authoritative — caller's responsibility to choose unique keys).

Out of scope

  • Producer-signed nonces (heavier; needs producer key infrastructure).
  • Cross-router deduplication.

See discussion in hark conversation on the dialect-distribution threat model.

The ingress handler currently reads the `Idempotency-Key` header and passes it through to the dispatcher (`cbcl-ingress-handler.lfe:39`), but the dispatcher doesn't *use* it for replay protection — every POST mints a fresh receipt regardless of whether the same key has been seen recently. An attacker who captures a `(lang elf (ask @router "do-work" …))` from a network trace can re-POST it and the work re-dispatches. ## Mitigation Treat `Idempotency-Key` as authoritative when present: - If `(principal, key)` has been submitted in the last N minutes (window TBD; suggest 1h), return the prior `{receipt_id, seq, capability, dispatched_to}` instead of dispatching again. - If absent, behave as today (mint fresh receipt) — keeps the path open for clients that don't care. Storage can be a small in-memory ETS table with TTL eviction (or piggyback on `cbcl-storage`). Keyed by `(principal, key)` so different principals using the same string don't collide. ## Acceptance - [ ] `cbcl-ingress-handler` (or a new `cbcl-idempotency-store` gen_server) caches `(principal, key) → ReceiptResponse` with TTL. - [ ] On repeat: return cached response, *do not* re-call `cbcl-dispatcher:submit`. - [ ] Absent `Idempotency-Key`: behaviour unchanged. - [ ] TTL configurable; default 3600 seconds. - [ ] Tests: - First POST returns fresh receipt. - Same (principal, key) within TTL returns identical receipt and does not re-dispatch. - Different principal, same key: independent receipts. - Same key, different body: still returns cached response (key is authoritative — caller's responsibility to choose unique keys). ## Out of scope - Producer-signed nonces (heavier; needs producer key infrastructure). - Cross-router deduplication. See discussion in hark conversation on the dialect-distribution threat model.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
anuna-research/cbcl-router#7
No description provided.