{"record":{"id":"cc6f6bf611da5439","repo":"Significant-Gravitas/AutoGPT","slug":"invalid-webhook-signature","errorCode":null,"errorMessage":"Invalid webhook signature","messagePattern":"Invalid webhook signature","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"autogpt_platform/backend/backend/api/features/integrations/router.py","lineNumber":734,"sourceCode":"            if webhook.credentials_id\n            else None\n        )\n    except NotFoundError as e:\n        logger.warning(f\"Webhook payload received for unknown webhook #{webhook_id}\")\n        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n\n    # Run provider signature verification (no-op for providers whose protocol\n    # has no signing scheme). 403 on failure; not 404 — that would leak\n    # webhook existence.\n    try:\n        await webhook_manager.verify_signature(webhook, request)\n    except HTTPException:\n        raise\n    except Exception:\n        logger.exception(\n            f\"Signature verification failed for webhook #{webhook_id} ({provider.value})\"\n        )\n        raise HTTPException(\n            status_code=status.HTTP_403_FORBIDDEN,\n            detail=\"Invalid webhook signature\",\n        )\n\n    payload, event_type = await webhook_manager.validate_payload(\n        webhook, request, credentials\n    )\n    logger.debug(\n        f\"Validated {provider.value} {webhook.webhook_type} {event_type} event \"\n        f\"with payload {payload}\"\n    )\n\n    webhook_event = WebhookEvent(\n        provider=provider,\n        webhook_id=webhook_id,\n        event_type=event_type,\n        payload=payload,\n    )","sourceCodeStart":716,"sourceCodeEnd":752,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/integrations/router.py#L716-L752","documentation":"The ingress endpoint calls webhook_manager.verify_signature(webhook, request) to check the provider's request-signature scheme (HMAC, RSA, etc.). Any non-HTTPException failure from verification — bad signature, missing signature header, malformed payload digest — is logged and converted to HTTP 403 'Invalid webhook signature'. A 403 (not 404) is deliberate: at this point the webhook's existence is already proven to the sender, so concealment adds nothing, and 403 correctly tells the sender its credentials for signing are wrong.","triggerScenarios":"Provider sends the event without the expected signature header (X-Hub-Signature-256 for GitHub, X-Signature-Ed25519 for Slack, X-Signature for Compass, etc.); the shared secret rotated and AutoGPT still verifies with the old one; a proxy/load balancer re-encodes or truncates the raw body so the digest doesn't match; manual curl testing without computing a signature.","commonSituations":"Testing ingress locally with plain curl; secret rotation on the provider side; reverse proxy (nginx/Cloudflare) modifying the body (e.g. chunked re-encoding) breaking HMAC; clock/lag between webhook registration and provider config propagation.","solutions":["Replay the exact raw request body with the correct signature header computed over the untouched bytes (signature is computed on the raw body, not parsed JSON).","If the secret rotated, re-register the webhook so a fresh secret is configured at the provider and stored server-side.","Ensure any proxy in front of the backend passes the body through byte-identical and forwards the signature headers.","Check backend logs: the logger.exception line 'Signature verification failed for webhook #{id}' includes the underlying exception with the precise reason."],"exampleFix":"# before (unsigned test call)\ncurl -X POST https://api.example.com/integrations/github/webhooks/$ID/webhook -d '{\"foo\":1}'\n\n# after (sign the raw body)\nBODY=$(cat event.json)\nSIG=$(python -c \"import hmac,hashlib,sys;print(hmac.new(b'$SECRET',sys.stdin.buffer.read(),hashlib.sha256).hexdigest())\" <<<\"$BODY\")\ncurl -X POST https://api.example.com/integrations/github/webhooks/$ID/webhook \\\n  -H \"X-Hub-Signature-256: sha256=$SIG\" \\\n  -H \"Content-Type: application/json\" \\\n  -d \"$BODY\"","handlingStrategy":"try-catch","validationCode":"# Pre-flight: compute the signature the way the provider will\nimport hmac, hashlib\nsig = \"sha256=\" + hmac.new(secret, raw_body_bytes, hashlib.sha256).hexdigest()\nassert sig == sent_header  # self-check before relying on the receiver","typeGuard":null,"tryCatchPattern":"# Only sane for test harnesses sending ingress traffic\nresp = await client.post(ingress_url, content=raw, headers=sig_headers)\ntry:\n    resp.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 403:\n        raise SignatureMisconfigured(ingress_url) from e  # fix secret/transport, don't retry\n    raise","preventionTips":["Sign the exact raw bytes of the body; never re-serialize parsed JSON for signature computation.","Keep proxies from rewriting the request body (disable modification/normalization).","Re-register the webhook whenever its secret rotates so both sides agree."],"tags":["webhook","http-403","signature-verification","security"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}