What is an HMAC signature and how to verify webhooks
An HMAC signature is a code computed from a message and a shared secret that proves the message was sent by someone who knows the secret and was not altered.
HMAC (hash-based message authentication code) combines a secret key with a message using a cryptographic hash function, usually SHA-256. The sender computes HMAC-SHA256 of the request body with a secret known only to the sender and the receiver, and sends the result in a header. The receiver repeats the calculation with the same secret and compares the results. If they match, the message is authentic and unchanged.
For payment webhooks, this is essential. A webhook URL is just an address on your server; anyone who finds it can send a fake “payment completed” request. Without signature verification, an attacker could unlock orders without paying. With HMAC, a forged request will not have a valid signature because the attacker does not know the secret.
Correct verification has several details. The signature must be computed over the raw request body exactly as received, not over JSON that your framework has already parsed and re-serialised, because key order or whitespace may change. Comparison should use a constant-time function, such as crypto.timingSafeEqual in Node.js, to avoid timing attacks. Many providers also include a timestamp in the signed data so that old requests cannot be replayed.
Typical bugs: verifying the parsed body, which fails intermittently; using the API key instead of the webhook secret; committing the secret to a repository; skipping verification in production “temporarily”; and comparing strings with a regular equality operator. If the secret leaks, rotate it immediately.
Moonvex signs every webhook with HMAC and sends the signature in the Moonvex-Signature header. Verify it with your webhook secret before trusting any event.