~learn.
0:00 / 1:19
W02 · Failure-first design

A six-month-old refund webhook, replayed, with a valid signature

transcript

A refund webhook arrives. The signature checks out perfectly. It was sent six months ago. And the refund runs again.

Webhooks get signed. Each subscription shares a secret with you. You run HMAC SHA 256 over the payload, with that secret. The signature rides in a header. The receiver recomputes it. Match? It's really from you.

But here's the catch. An attacker intercepts an order refunded webhook. They wait. Six months. Then send it again. Not one byte changed. The signature is still perfectly valid. The receiver processes the refund again.

Sign only the body, and a signature is valid forever. So sign the timestamp and the body together. The receiver rejects anything more than 5 minutes off. And the attacker can't just change the time. It's inside the signature.

Two details. Send exactly the bytes you signed. Re-serialise the JSON, and spacing changes. The HMAC won't match. And compare signatures in constant time, so timing can't leak them.

A signature proves who sent it. Not when. Sign the time with the body, and an old message stays old.

Full breakdown in the lecture.