T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/webhook_server.py:20
- Finding
- Webhook Authentication Fails Open for Unsigned Requests<![CDATA[ ## Vulnerability Details **File Location**: `scripts/webhook_server.py:20-24`, `scripts/webhook_server.py:43-48` **Vulnerability Type**: Authentication bypass caused by fail-open signature verification **Risk Level**: High ### Complete Code Snippet ```python def verify_signature(payload_bytes: bytes, signature: str, secret: str = WEBHOOK_SECRET) -> bool: """Verify HMAC-SHA256 signature from provider.""" if not secret: return True # Skip verification if no secret configured expected = hmac.new(secret.encode(), payload_bytes, hashlib.sha256).hexdigest() return hmac.compare_digest(f"sha256={expected}", signature) ``` ```python signature = self.headers.get("X-Hub-Signature-256", "") or \ self.headers.get("X-Signature-256", "") or \ self.headers.get("X-Slack-Signature", "") if signature and not verify_signature(body, signature, WEBHOOK_SECRET): logger.warning("Invalid signature — rejecting request") self.send_response(401) self.end_headers() return ``` ### Technical Analysis Signature verification is only performed when the request contains a recognized signature header. If the header is absent, the condition evaluates to false and request processing continues. In addition, `verify_signature()` explicitly returns `True` when no secret is configured. This creates two fail-open states: 1. A client can omit the signature header even when the server has a secret. 2. All verification is disabled when the secret file and `WEBHOOK_SECRET` environment variable are absent. Because the server binds to all network interfaces, this flaw can allow unauthenticated remote clients to submit forged events. ### Attack Path 1. An attacker identifies the webhook service listening on port 8443. 2. The attacker sends a JSON POST request without any recognized signature header. 3. The `if signature and ...` condition is skipped because `signature` is empty. 4. The attacker supplies an event type through ...[truncated 682 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Refuse to start the service unless a nonempty webhook secret is configured. - Reject requests that do not contain the mandatory signature header for the selected provider. - Change the authentication flow to fail closed: ```python if not WEBHOOK_SECRET: raise RuntimeError("WEBHOOK_SECRET must be configured") if not signature or not verify_signature(body, signature, WEBHOOK_SECRET): self.send_response(401) self.end_headers() return ``` - Determine the provider from a trusted endpoint configuration rather than attacker-controlled headers. - Use separate endpoints and secrets for GitHub, Slack, and Stripe. - Add tests confirming that missing, empty, malformed, and invalid signatures all receive an HTTP 401 response. - Restrict network exposure with a reverse proxy, firewall, or allowlist where operationally possible. ]]>
