Onfido logo home page
Get in touch
Try it now
Arrow back Back to guides

Manual webhook signature verification

Webhook security

For API v3 users, we recommend you use the built-in support for webhook verification in our client libraries.

If you're unable to do so (for example, you're using API v2), you can follow the instructions on this page.

Extract the signature and event body

Each webhook event will have an X-SHA2-Signature header. This contains the webhook event’s signature in hexadecimal format.

Make sure you use the raw event request body. If you parse it from JSON first, the fields may be reordered.

Compute the expected signature

You can get the expected signature of the webhook by computing the HMAC of the event request body using the SHA256 algorithm, using the webhook’s secret token as the key.

Compare the signatures

A webhook event is valid if the signature from the header is equal to the expected signature you compute for it. Make sure signatures are both in binary, or both in hexadecimal, before comparing.

You should use a constant time equality function to prevent timing attacks. A timing attack is where a malicious user measures the small time differences taken to compare the signatures over many requests, to eventually work out the expected signature for a webhook event. A constant time equality function prevents this by always taking the same amount of time when comparing strings of a particular length.

Example pseudocode

You may find the following pseudocode useful:

def verify_webhook_event_signature(webhook_event_request)
  signature_header_hexadecimal = webhook_event_request.get_header("x-sha2-signature")

  # Make sure to use the raw event body and not to convert it to an object first.
  raw_event_body = webhook_event_request.body_as_string

  # Compute the HMAC using the SHA256 algorithm and using your webhook's token as the key.
  expected_signature = hmac(algorithm="SHA256", key=webhook_token, data=raw_event_body)

  # Make sure signatures are both in binary, or both in hexadecimal, before comparing.
  signature_header = decode_hexadecimal(signature_header_hexadecimal)

  # Use a constant time equality function to prevent timing attacks.
  if constant_time_equals(signature_header, expected_signature)
    # It is now safe to parse the event body.
    return parse_json(raw_event_body)
  else
    # Handle an invalid webhook event.
    error("Invalid webhook event")
Onfido

Our solutions

Onfido uses 256-bit SSL encryption 100% of the time on every device.

BSI ISO/IEC27001

Onfido has been certified by BSI to ISO 27001 under certificate number IS 660122.

© Onfido™, 2022. All rights reserved.
Company Registration Number: 07479524.