Skip to main content

Webhook Signature

Webhook Signing Mechanism

To ensure the authenticity and integrity of webhook calls, Zyphe uses a signing mechanism based on HMAC (Hash-based Message Authentication Code) with SHA256. This allows you to verify that the webhook payload has not been tampered with and genuinely originates from the Zyphe platform.

How it Works

  1. Secret Key: Each organization is assigned a unique secret key. This key is crucial for both signing and verifying the webhook payloads. Keep this secret key confidential.
  2. Timestamp: A Unix timestamp (number of seconds since the Unix epoch, UTC) is generated at the time the webhook is sent.
  3. Signed Payload Construction: The raw JSON payload of the webhook request is combined with the timestamp. Specifically, the timestamp is prepended to the payload, separated by a . (dot).
signed_payload = "{timestamp}.{raw_json_payload}"

For example, if the timestamp is 1678886400 and the raw payload is {"event":"user.created", "data":{"id":"123"}}, the signed_payload string that will be signed will be:

1678886400.{"event":"user.created", "data":{"id":"123"}}

HMAC Generation:

  • The secret key (provided as a hexadecimal string) is first decoded into its raw byte representation.
  • An HMAC-SHA256 algorithm is initialized using this decoded secret key.
  • The raw bytes of the signed_payload (including the timestamp and the dot separator) are then fed into the HMAC algorithm.
  • The HMAC algorithm computes a unique message authentication code based on the secret key and the combined signed_payload.
  1. Signature Header: The resulting HMAC value is then encoded into a hexadecimal string and included in the webhook request's x-signature header. The signature header also include the timestamp and is formatted the following way:

t=<timestamp>.v0=<signature_hex>

Verifying Webhook Signatures

When your application receives a webhook, you should perform the following steps to verify its signature:

  1. Retrieve Headers:

    • Get the signature value from the x-signature header.
    • Extract the timestamp and the actual signature hex from the x-signature header
  2. Retrieve the Payload: Obtain the raw, unmodified body of the webhook request. It is critical to use the exact raw body received, without any parsing or re-serialization, as even subtle changes can cause the signature verification to fail.

  3. Construct the Signed Payload: Combine the retrieved timestamp and the raw webhook payload in the exact same format used by Zyphe:

    local_signed_payload = "{received_timestamp}.{raw_request_body}"
  4. Calculate Your Own Signature: Using your organization's secret key and the local_signed_payload constructed in the previous step, compute an HMAC-SHA256 signature locally, following the same steps described in the "HMAC Generation" section above.

  5. Compare Signatures: Compare your computed signature (hexadecimal string) with the signature received in the x-signature header.

    1. If they match, you can be confident that the webhook is authentic and untampered.
    2. If they do not match, do not process the webhook, as it may be fraudulent or corrupted.
  6. Timestamp Verification (Recommended for Replay Attack Mitigation): As an additional security measure, you should verify the timestamp to mitigate replay attacks:

    • Parse the timestamp collected from the x-signature header as an integer representing seconds since the Unix epoch.
    • Compare this timestamp with your server's current UTC time.
    • Reject webhooks where the timestamp is significantly older (e.g., more than 5 minutes) or significantly in the future. This ensures that old, intercepted webhooks cannot be replayed successfully.