Implement the webhook event signature validation
Low difficulty
5 min read time
15 min implementation time
How to validate the signature
When you subscribe to a webhook event to receive signal updates from a vehicle, you will receive POST requests with the X-Hub-Signature
header.
This header represents the message signature that allows checking the authenticity of the request.
How can we validate it?
We need to check if the signature is the concatenation of two strings separated by the =
character:
- The first string has to be the name of a hash function for example,
MD5
,SHA1
,SHA256
or others. Let us call it algorithm.
Info
This system currently uses the
SHA256
hash function.
- The second string is the
HMAC
using the hash function named in the first string.
Let us call it HMAC(message, secret, algorithm).
The signature will have a form like the following:
algorithm=HMAC(message, secret, algorithm)
.
Info
If you don't remember what we are talking about, please visit the Receiving signal guide's page.
What is HMAC
In cryptography, an HMAC is a specific type of message authentication code, MAC, involving a cryptographic hash function and a secret cryptographic key.
To validate the HMAC
, we need to generate it using the following parameters:
- A message that is the body of the POST request we are receiving.
- A secret, that is the ‘hub.secret’ property we used in the body of the request made at the moment of the webhook subscription.
- A hash function, which is written in the first string of the signature as explained before.
Warning!
Both the secret and message have to be encoded in
utf-8
.
Be careful when parsing the message, the body of the request in our case, because it has to be a text!
Code
import * as crypto from "crypto";
const supportedAlgorithms = ["sha256"];
const isSignatureValid = (message: string, secret: string, signature: string): boolean => {
const parts = signature.split("=");
if (parts.length !== 2) {
return false;
}
const algorithm = parts[0];
if (!supportedAlgorithms.includes(algorithm)) {
return false;
}
const hmac = crypto.createHmac(algorithm, secret)
.update(message, "utf8")
.digest();
const signatureBuffer = Buffer.from(parts[1], "hex");
return crypto.timingSafeEqual(signatureBuffer, hmac);
};
How this example works
If you want to have feedback if you are implementing it in the right way, here we show a practical example of a signature generated from the following parameters:
- Message:
{"topic":"vehicle:7d42d670-6a96-4ff0-ab63-5d6673967d2d:generic:autonomy_meters","payload":{"data":{"meters":24000},"timestamp":1614594977551,"deliveryTimestamp":1614594977563}}
- Secret:
this_is_a_$ecret
. - Hash function:
sha256
.
The generated signature will be: sha256=bb2c166d254838b72bd78b0486d804cef58bd36c987d12147d554b45700e69f4
.
Congrats!
Now, you have a simple method to validate a signature.
Updated almost 2 years ago