Skip to content

Instantly share code, notes, and snippets.

@alejoar
Last active September 24, 2022 11:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alejoar/a07b22be2075546095c43e2f03fa6f56 to your computer and use it in GitHub Desktop.
Save alejoar/a07b22be2075546095c43e2f03fa6f56 to your computer and use it in GitHub Desktop.
[Starlette/Fastapi] Webhook signature validation for Arcadia
import hashlib
import hmac
import time
import starlette
WEBHOOK_SIGNING_KEY = "<YOUR_WEBHOOK_SIGNING_KEY>"
SECONDS_TO_STALE = 300 # 5 minutes
class ArcadiaSignatureException(Exception):
pass
async def validate_webhook_signature(req: starlette.requests.Request):
req_body = await req.body()
now_ts = time.time()
# 1. Extract the timestamp and signatures from the header
webhook_ts = int(req.headers.get("Arc-Webhook-Timestamp", 0))
webhook_signature = req.headers.get("Arc-Webhook-Signature", "")
if not webhook_ts or not webhook_signature:
raise ArcadiaSignatureException
# 2. Prepare the payload string by concatenating the timestamp with the body
payload_to_sign = f"{webhook_ts}.{req_body.decode()}"
# 3. Calculate the Signature using your Arcadia Webhook Secret
signature = hmac.new(
bytes(WEBHOOK_SIGNING_KEY, "utf-8"), msg=bytes(payload_to_sign, "utf-8"), digestmod=hashlib.sha256
).hexdigest()
# 4. If the timestamp is older than the threshold, this may be a replay attack
if abs(now_ts - webhook_ts) > SECONDS_TO_STALE:
raise ArcadiaSignatureException
# 5. If the signatures don't match, this may be a fraudulent webhook
# Be sure to use a constant time string comparison algorithm to prevent timing attacks
if not hmac.compare_digest(signature, webhook_signature):
raise ArcadiaSignatureException
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment