{"record":{"id":"3571c9e371c0a506","repo":"sickn33/agentic-awesome-skills","slug":"invalid-signature","errorCode":null,"errorMessage":"Invalid signature","messagePattern":"Invalid signature","errorType":"http","errorClass":null,"httpStatus":401,"severity":"critical","filePath":"skills/whatsapp-cloud-api/assets/boilerplate/python/webhook_handler.py","lineNumber":39,"sourceCode":"    Validar esta assinatura previne requests falsificados.\n    Usa hmac.compare_digest para comparacao constant-time (previne timing attacks).\n    \"\"\"\n    secret = app_secret or os.environ[\"APP_SECRET\"]\n\n    def decorator(f):\n        @wraps(f)\n        def decorated_function(*args, **kwargs):\n            signature = request.headers.get(\"X-Hub-Signature-256\", \"\")\n            if not signature:\n                abort(401, \"Missing signature header\")\n\n            raw_body = request.get_data()\n            expected = \"sha256=\" + hmac.new(\n                secret.encode(), raw_body, hashlib.sha256\n            ).hexdigest()\n\n            if not hmac.compare_digest(signature, expected):\n                abort(401, \"Invalid signature\")\n\n            return f(*args, **kwargs)\n\n        return decorated_function\n\n    return decorator\n\n\ndef verify_webhook(verify_token: str | None = None):\n    \"\"\"\n    Handle webhook verification (GET request from Meta).\n    Returns the challenge to confirm the webhook endpoint.\n    \"\"\"\n    token = verify_token or os.environ[\"VERIFY_TOKEN\"]\n\n    mode = request.args.get(\"hub.mode\")\n    req_token = request.args.get(\"hub.verify_token\")\n    challenge = request.args.get(\"hub.challenge\")","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/sickn33/agentic-awesome-skills/blob/58d857988fcfac6986206bca2b2fe223aa437e4b/skills/whatsapp-cloud-api/assets/boilerplate/python/webhook_handler.py#L21-L57","documentation":"The decorator recomputed the expected HMAC over the raw request body with the configured app secret, and hmac.compare_digest found it different from the X-Hub-Signature-256 header. A mismatch means the bytes verified are not the bytes Meta signed, or the secret used for verification differs from the secret used for signing. The request is aborted with 401 before the Flask handler runs.","triggerScenarios":"WHATSAPP_APP_SECRET in the environment differs from the Meta app's App Secret; the raw body was consumed or re-encoded by earlier middleware so request.get_data() returns altered bytes; signature computed over pretty-printed or re-serialized JSON instead of the exact payload; header mangled by an intermediary.","commonSituations":"App secret rotated in the Meta developer console but not redeployed; secret copied with trailing whitespace, quotes, or from the wrong app; a body-parsing hook decoding/re-encoding the stream before verification; multiple environments (test/prod) pointing at different Meta apps.","solutions":["Compare the deployed app secret byte-for-byte with Meta App Dashboard > App Settings > Basic > App Secret","Make the HMAC decorator the first thing to touch the request so get_data() returns the exact bytes Meta signed","If the secret was recently rotated, redeploy all instances and confirm every environment uses the new value"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# In tests, sign the exact bytes you send\nbody = json.dumps(payload).encode()\nsig = 'sha256=' + hmac.new(APP_SECRET.encode(), body, hashlib.sha256).hexdigest()\nclient.post('/webhook', data=body, headers={'X-Hub-Signature-256': sig})","typeGuard":null,"tryCatchPattern":"try:\n    verify_signature(request)\nexcept Exception:\n    # log body length and a secret fingerprint (never the secret) to diagnose mismatches\n    raise","preventionTips":["Keep one source of truth for the app secret (secret manager), never hand-copied .env files","Never let body-parsing middleware run before signature verification"],"tags":["webhook","whatsapp","flask","hmac","signature-mismatch","secret-mismatch"],"backgroundTag":"webhook-signature-verification-failed","analyzedSha":"58d857988fcfac6986206bca2b2fe223aa437e4b","analyzedAt":"2026-08-26T11:55:59.350Z","schemaVersion":2},"datasetVersion":"2026-08-26T14:46:13.012Z"}