Verifying webhook authenticity
Einführung
Webhooks provide an additional security measure to verify that the webhook is genuine and has come from Zendesk. This can be useful if you're looking to ensure only Zendesk webhooks are being made to your endpoint and ensure the information is genuine and as the system expects. These signatures will also help prevent against replay attacks. Verifying the signing secret is optional.
Webhook requests will contain two headers which can be used to verify the request's authenticity:
X-Zendesk-Webhook-Signature
- the main signatureX-Zendesk-Webhook-Signature-Timestamp
- the timestamp used to verify the signature
This is used in conjunction with the payload of the request (if there is one).
Verifying the signature
Sign the body and signature timestamp with the webhook secret key using SHA256, then base64 encoding the resulting digest.
Represented simply:
base64(HMACSHA256(TIMESTAMP + BODY))
To verify the signature, create the same SHA256 HMAC signature and then compare it to the webhook payload to ensure that they match. If they match, then you can be sure that the webhook came from Zendesk. If they don't, it may be a request from another source.
Not all requests from webhooks will have a body (GETs, DELETEs), so ensure that this scenario is accounted for in any verification code. Depending on language, this may be an empty string or null. Consult your language's documentation for details.
NodeJS verification example
The example below shows how a signature can be verified using NodeJS and Express. In a production environment, this check should form part of the check to accept or reject the webhook request.
const express = require("express");
const crypto = require("crypto");
require("body-parser-xml")(express);
// Signing secret from webhook itself
const SIGNING_SECRET = "dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==";
// Always sha256
const SIGNING_SECRET_ALGORITHM = "sha256";
const PORT = 3000;
const app = express();
function isValidSignature(signature, body, timestamp) {
let hmac = crypto.createHmac(SIGNING_SECRET_ALGORITHM, SIGNING_SECRET);
let sig = hmac.update(timestamp + body).digest("base64");
return (
Buffer.compare(
Buffer.from(signature),
Buffer.from(sig.toString("base64"))
) === 0
);
}
function storeRawBody(req, res, buf) {
if (buf && buf.length) {
req.rawBody = buf.toString("utf8");
}
}
// Use middleware to store raw request body depending on request format
app.use(
express.json({
verify: storeRawBody,
})
);
app.use(express.urlencoded({ verify: storeRawBody, extended: true }));
app.use(express.xml({ verify: storeRawBody }));
app.post("/hook", (req, res) => {
// Fields from the webhook request, this will change every time
const signature = req.headers["x-zendesk-webhook-signature"];
const timestamp = req.headers["x-zendesk-webhook-signature-timestamp"];
const body = req.rawBody;
console.log(
isValidSignature(signature, body, timestamp)
? "HMAC signature is valid"
: "HMAC signature is invalid"
);
res.status(200).send("Success");
});
app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`));
Retrieving a webhook's signing secret key
In the UI
Navigate to the webhook in Admin Center. On the webhook details page, the secret key is hidden by default. It can be revealed by clicking "Reveal secret".
In the API
Use the Show webhook signing secret API to retrieve the secret.
Note: The secret key should be treated like any other credential and should not be committed to code or exposed publicly.
Resetting the secret key
Resetting the secret key will generate a new secret key that will be used for new webhook requests from that time on. You should ensure the new key is updated on your server after changing it, as new requests may not pass verification.
Signing secrets on new webhooks
You may need to test the webhook before it is created. Signing secrets are only generated once a webhook is fully created. This means that to test a webhook during creation, you'll need to use a static signing secret.
The static secret for verifying a test webhook before it is created will always be: dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==
Note: You'll still need to verify the signature using this secret to validate your testing.
Once the webhook is created, a separate signing secret will be generated for it. Existing webhooks being updated and tested will use the proper signing secret for the webhook, not the static secret.
Defining a webhook's signing secret key in app requirements
For apps that require a webhook, app developers can define a 16-64 character alphanumeric secret key in their apps requirement. That key will be used for webhooks created by their app for use in signature verification.
Unlike secret keys for webhooks created in other ways, secret keys defined in app requirements can't be viewed or reset by Zendesk admins.