In a production setting, it is important to verify signatures sent as part of the webhook payloads. Verifying these signatures ensures that the payloads were sent by One Codex and not a malicious third party.
X-OneCodex-Signature HTTP header with all delivered webhooks. These signatures are generated using a hash-based message authentication code (HMAC) with SHA-256. Here’s an example header:
X-OneCodex-Signature: t=1492774577c v1=d929ba98ac0e56ff425f9b8ed7c7ab631dc680f9ea80ce2f604cc75580a63b53
t= provides a Unix timestamp and v1= provides the v1 signature (currently the only signature scheme). The signature uses a webhook secret (defaults to the API key for your account) to sign the POST payload body and timestamp. To verify the signature of the payload, you need to:
- Extract the timestamp and signature from the headers
- Concatenate the the timestamp and request payload with a .to generate a signed payload
- Determine the expected signature; and finally
- Verify that the expected signature matches the received signature
Some brief Python 3 code for validating the signature is included for demonstration purposes below:import hashlib
import hmac
import json
from flask import request
# Parse the request using your web framework of choice.
# Note that the entire body of the request is the payload
# and that you may need to parse the raw request body
# vs. any loaded JSON in a different language or framework
# (this code assumes a Python Flask request object).
payload = request.json
header = request.headers.get("X-OneCodex-Signature")
# 1. Extract the timestamp and signature
timestamp_part = header.split(" ")[0]
signature_part = header.split(" ")[1]
if not timestamp_part.startswith("t=") or not signature_part.startswith("v1="):
    raise Exception("Bad signature header format")
timestamp = int(timestamp_part.split("=")[1])
signature = signature_part.split("=")[1]
# 2. Generate a concatenated signed payload
signed_payload = "%d.%s" % (timestamp, payload)
# 3. Compute the SHA256 hash of your secret
secret = hashlib.sha256("YOUR_WEBHOOK_SECRET".encode("utf-8")).hexdigest().encode("utf-8")
# 4. Determine the expected signature
expected_signature = hmac.new(
    secret,
    signed_payload.encode(),
    digestmod=hashlib.sha256
).hexdigest()
# 4. Verify that the expected signature matches
assert expected_signature == signature
In the near future, we plan to add support for parsing Event objects and verifying the signatures from a webhook payload in our onecodex Python library. This will offer an easy, one line mechanism for verifying payload POST bodies sent by our platform.