Back to Technical Mode

HMAC Request Signing

HMAC (Hash-based Message Authentication Code) combines a shared secret with the request contents to produce a signature that changes completely if even one character of the request changes.

The signature covers the method, path, body, and a timestamp — not just the body
Changing a single character anywhere in the signed content invalidates the whole signature
The shared secret never travels in the request — only the resulting signature does
This defeats tampering even over a channel where TLS has already been stripped or intercepted

Want this flow explained in detail as it plays?

POST/api/orders

X-Timestamp: 1735689600

X-Signature (original, sent by the real client):

Body (tamper with it — the signature above won't update, because an attacker doesn't know the shared secret)

Loading...

Defense in depth

  • Server recomputes HMAC-SHA256(method + path + body + timestamp, shared_secret) and compares to X-Signature
  • Any mismatch, however small the change, is rejected with 403
  • The timestamp is also checked for freshness so an old signed request can't be resubmitted indefinitely

Signing vs. Verification

import { createHmac } from "crypto";

function sign(method: string, path: string, body: string, ts: string, secret: string) {
  return createHmac("sha256", secret)
    .update(`${method}${path}${body}${ts}`)
    .digest("hex");
}