import hmac import hashlib import time import logging logger = logging.getLogger(__name__) SIGNATURE_VERSION = "v0" WEBHOOK_TOLERANCE_SECONDS = 300 def verify_webhook_signature( raw_body: bytes, signature_header: str, timestamp_header: str, webhook_secret: str, tolerance_seconds: int = WEBHOOK_TOLERANCE_SECONDS, ) -> bool: if not signature_header or not signature_header.startswith(f"{SIGNATURE_VERSION}="): logger.warning("Webhook signature header missing or invalid prefix") return False expected_hex = signature_header[len(SIGNATURE_VERSION) + 1 :] try: request_ts = int(timestamp_header) except (ValueError, TypeError): logger.warning("Webhook timestamp header invalid: %s", timestamp_header) return False current_ts = int(time.time()) if abs(current_ts - request_ts) > tolerance_seconds: logger.warning( "Webhook timestamp out of tolerance: request=%s, current=%s, diff=%s", request_ts, current_ts, abs(current_ts - request_ts), ) return False message = f"{timestamp_header}.{raw_body.decode('utf-8')}" computed_hmac = hmac.new( webhook_secret.encode("utf-8"), message.encode("utf-8"), hashlib.sha256, ).hexdigest() return hmac.compare_digest(computed_hmac, expected_hex)