A practice prompt we wrote. No company or candidate report names it, so it carries no company tag.

How to answer

Three checks, in a deliberate order, and one distinction most answers blur: replay protection is not deduplication.

  1. Pin the provider’s scheme first. What exactly is signed, with which algorithm, where the timestamp lives, and whether a retry is signed again. Stripe’s manual verification steps are a clean reference shape: a header carrying a timestamp and one or more v1 signatures, an HMAC-SHA256 over the timestamp, a dot and the raw body, and a constant-time comparison.
  2. Verify the raw bytes. Read the body before any JSON parsing. A parsed and re-serialized body differs in whitespace and key order, and the check fails for reasons that look random.
  3. Order the checks by cost and trust. Parse the header strictly, then the timestamp window in both directions, then the HMAC with hmac.compare_digest. Say why: == can return as soon as it finds a difference, so response times can leak how much of a forged signature is right. The timestamp only helps because it is inside the MAC.
  4. Add the replay store last. The window still lets a captured request be replayed until it closes. Record each accepted request with one atomic set-if-absent and a TTL that covers the whole window; older copies fail the timestamp check, so the store stays bounded. Only authenticated requests write to it, so junk cannot fill it.
  5. Separate it from event dedupe. Stripe’s replay section says each retry gets a new signature and timestamp, so a provider retry passes the replay store; the event-id dedupe that the handler already needs catches it.
  6. Fail safely. A bare 400 to the caller and the reason in your logs. If the store is down, return 503 so the provider retries.

The trap is a correct HMAC with no replay story.

Follow-ups

What the interviewer may ask next, once your first answer is on the table.

  • The provider is rotating its signing secret. How does your endpoint keep accepting requests during the change?
  • Your replay store is unreachable. Do you accept the request, reject it, or something else?
  • The provider retries a failed delivery with the same timestamp and signature. What breaks in your design, and what do you change?
  • Your framework parses JSON before your handler runs. How do you get the bytes you need?

Where answers go wrong

  • Comparing signatures with ==, or verifying a re-serialized JSON body instead of the raw bytes the provider signed.
  • Checking the timestamp without it being inside the signed payload, so an attacker can replace it with a fresh one.
  • Treating the timestamp window as replay protection, when any captured request can be replayed until the window closes.
  • A replay check that reads the store and then writes it in two calls, so two copies arriving together both pass.

Answer this in two minutes

Write the answer you would say out loud. The clock starts with your first word.

Two minutes

Model answer

“I’ll assume the provider sends Webhook-Signature: t=<unix seconds>,v1=<hex> and signs the timestamp, a dot and the raw body with HMAC-SHA256. During a secret rotation it may send two v1 values, and we may hold two secrets.