Checking Webhook Signatures
Prefinery signs the webhook events it sends to your endpoints by including a signature in each event's
X-Prefinery-Signature header. This allows you to verify that the events were sent by Prefinery, not by a third party.
Before you can verify the signature, you need to retrieve your project's webhook signing secret from inside your account on the Integrations > Webhooks page. Click the View secret button in order to display your project's unique signing secret.
Verifying the signature
X-Prefinery-Signature header included in each signed event contains a timestamp and a signature. The timestamp
is prefixed by
t=, and the signature is prefixed by a scheme. Schemes start with
v, followed by an integer. Currently,
the only valid live signature scheme is
Step 1: Extract the timestamp and signatures from the header
Split the header, using the
, character as the separator, to get a list of elements. Then split each element, using the
= character as the separator, to get a prefix and value pair.
The value for the prefix
t corresponds to the timestamp, and
v1 corresponds to the signature. You can
discard all other elements.
Step 2: Prepare the signed_payload string
signed_payload string is created by concatenating:
- The timestamp
- The character
- The actual JSON payload (i.e., the request body)
Step 3: Determine the expected signature
Compute an HMAC with the SHA256 hash function. Use the project's signing secret as the key, and use the
string as the message.
Step 4: Compare the signatures
Compare the signature in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.
To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures.
Preventing replay attacks
A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate
such attacks, Prefinery includes a timestamp in the
X-Prefinery-Signature header. Because this timestamp is part of the signed
payload, it is also verified by the signature, so an attacker cannot change the timestamp without invalidating the
signature. If the signature is valid but the timestamp is too old, you can have your application reject the payload.
Your tolerance for what it means to be too old is up to you. We recommend using Network Time Protocol (NTP) to ensure that your server's clock is accurate and synchronizes with the time on Prefinery's servers.
Prefinery generates the timestamp and signature each time an event is sent to your endpoint. If Prefinery retries an event (e.g., your endpoint previously replied with a non-200 status code), then a new signature and timestamp is generated for the new delivery attempt.