Webhooks are a common approach for third parties to notify their customers about events happening in real-time. FastAPI is one of the best API frameworks for Python, adding support for webhooks is fast and simple.
A common signing algorithm for webhooks is a SHA256 HMAC signature, but it's easy to write a common function to support md5, sha1, and sha256.
import hmac
from hashlib import md5, sha1, sha256
from functools import partial
def compare_signature(digestmod, key: bytes, message: bytes, expected_signature: str) -> bool:
mac = hmac.new(key, message, digestmod)
return hmac.compare_digest(mac.hexdigest(), expected_signature)
compare_md5_signature = partial(compare_signature, md5)
compare_sha1_signature = partial(compare_signature, sha1)
compare_sha256_signature = partial(compare_signature, sha256)
The most important part is to ensure the use of the hmac.compare_digest
function instead of simple string comparison (==
), this prevents timing attacks.
from fastapi import Request, Header, FastAPI, Depends, HTTPException
app = FastAPI()
# 32 bytes or more for the best security
WEBHOOK_SECRET = b"443c9cec-9dd6-11e9-b610-3c15c2eb7774"
async def _validate_webhook_signature(request: Request):
webhook_signature = request.headers.get("webhook-signature")
if not webhook_signature:
raise HTTPException(400, "Missing webhook signature header")
request_body = await request.body()
# CHANGE: compare_sha256_signature for the signature your integration uses
if not compare_sha256_signature(WEBHOOK_SECRET, request_body, webhook_signature):
raise HTTPException(401, "Invalid webhook signature")
@app.post("/my-webhook", dependencies=[Depends(_validate_webhook_signature)])
def my_webhook():
# ... process data
return {}
After creating the _validate_webhook_signature
function, it can be used on any webhook routes that are defined in the future. The general idea with a webhook signature is to ensure that whatever third-party service sending data to your webhook is who they say they are and not a malicious actor.
The most important concept to remember is that a webhook signature is a replacement for your normal app authentication but is not a replacement for authorization. This difference can be read about more in Appendix I on the Authentication page.