X-Kash-Signature).
The signature format
During the secret rotation overlap window the header may carry two
v1= entries (current + previous secret). Accept the request if any matches.
The verification algorithm
- Read the raw request body as a string. Don’t reparse JSON — the signature is over the exact bytes that were sent.
- Parse the header: split on
,, then on=, into a map. - Read
t(the timestamp). If|now - t| > 5 minutes, reject — the signature is too old (replay attack). - Compute
expected = HMAC_SHA256(secret, "${t}.${rawBody}")and hex-encode it. - For each
v1=entry in the header, compare withexpectedusing a constant-time comparator. If any matches, accept.
Reference implementations
TypeScript / Node.js (the SDK does this for you)
The simplest path — let@kashdao/sdk verify and parse in one line:
TypeScript / Node.js (no SDK)
Python
Go
Rust
Common mistakes
- Re-serialising the JSON body before verifying. The signature is over the exact bytes we POSTed. If you parse + re-stringify, whitespace differs and the signature fails. Always verify against the raw body, then parse JSON.
- Using
==instead of constant-time comparison.==short-circuits on the first differing byte and leaks signature bytes via timing. Usecrypto.timingSafeEqual(Node),hmac.compare_digest(Python),hmac.Equal(Go),ct_eq(Rust). - Skipping the timestamp freshness check. Without
tvalidation, an attacker who captures one signed payload can replay it forever. The 5-minute tolerance handles legitimate clock skew. - Ignoring multiple
v1=entries. During the rotation overlap window we send two — accept either. A picky verifier rejects valid traffic mid-rotation.
Testing your verifier locally
The CLI can sign a payload exactly like the production worker (t=<unix-ms>,v1=<hex>) and POST it to your endpoint:
--timestamp-ms to test your replay-window enforcement, and use kash webhooks verify for offline signature checks.
Reporting verification bugs
If the production worker is sending signatures your code can’t verify, please open a GitHub issue with:- The raw
X-Kash-Signatureheader value - The raw body bytes (or a hash of them)
- Your
webhook_secret’srotatedAttimestamp (don’t share the secret itself) - Your verification code
Next
Retries & Redelivery
What happens when your endpoint fails — and how to manually replay.
Secret Rotation
Rotate without losing a single delivery.