Signature
Overview
This document provides guidance on webhook message signatures and how to validate the X-Caf-Signature header that is included in all webhook requests made by Certta services.
Message signatures
Since webhook URLs are exposed to the internet, your application needs a secure mechanism to verify that requests are genuinely from Certta. We implement a signature validation header for this purpose, which we call the message signature.
Despite being an internal validation for your integration, rejecting requests with invalid signatures is part of the webhook validation process at Certta. We may randomly send events with invalid signatures to verify your integration continues to meet our validation criteria. In any case, this validation is in your interest to prevent fraud. We maintain audit trails of delivered events, delivery attempts, and discarded events.
Signature header
Each webhook request includes a X-Caf-Signature header with the following format:
X-Caf-Signature: 5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdThe header contains the HMAC SHA-256 signature value generated from the raw request body using your client secret.
How to validate
Certta implements a keyed-hash message authentication code (HMAC) mechanism with SHA256 to generate a signature for each message sent, with the final encoding in hexadecimal.
This signature is generated using your application's client secret (the same one used to generate tokens) found in Trust, the platform that manages the configurations. The signature is sent through the X-Caf-Signature header in each HTTP request.
To validate this signature, your integration should:
Generate the HMAC of the received message using your secret (stored in a secure location)
Compare it with the received signature using a secure comparison algorithm
Important security considerations
As fields can be added to any event at any time without being a breaking change, message validation should be done before the content is transformed into a language object. This means using the body's byte array "as-is" to generate the comparison signature, without any transformation.
This is also important because when dealing with JSONs, {"prop1": "value1", "prop2": "value2"} is equivalent to {"prop2": "value2", "prop1": "value1"} for parsers/encoders since property ordering is not part of a JSON definition, but the byte arrays formed by the two objects are different. Also, some characters may be encoded differently depending on the library or language being used.
Implementation examples
Many programming languages include secure HMAC implementations in their standard libraries:
Python: hmac module
Node.js: crypto.Hmac class
Ruby: OpenSSL::HMAC
Java: javax.crypto.Mac
Format variations
All these examples are valid for the same JSON but with different formatting and must be supported by your integration:
Without spaces or line breaks
The X-Caf-Signature value is calculated from these exact bytes and your webhook secret.
With spaces, no line breaks
Even though this JSON represents the same object, its signature differs because its raw bytes differ.
With spaces and line breaks
This formatted body also produces a different signature.
With properties in different order
Changing property order also changes the signature.
Code examples
Node.js
Java
Go
Security best practices
Always verify signatures - Never trust webhook requests without verifying their signatures
Process raw body bytes - Use the raw body bytes for signature verification, not parsed JSON
Use constant-time comparison - To prevent timing attacks, use a constant-time string comparison function
Keep your webhook secret secure - Never expose your webhook secret in client-side code
Implement idempotency - Process each webhook event only once, even if received multiple times
Obtaining your webhook secret
You can find your webhook secret in Trust, the platform that manages configurations, under the webhook configuration settings. If you believe your secret has been compromised, you can generate a new one at any time.
Last updated

