Webhook Abuse in Fastapi with Api Keys
Webhook Abuse in Fastapi with Api Keys — how this specific combination creates or exposes the vulnerability
Webhook abuse in a Fastapi service that uses API keys can occur when an endpoint that is protected only by an API key accepts external HTTP requests that trigger downstream actions. Because API keys are often passed as headers and sometimes stored in client-side code or logs, they can be harvested or leaked. If a webhook URL is publicly reachable and does not also enforce origin validation or a separate signer, an attacker who discovers or guesses the API key may be able to invoke the webhook endpoint and perform actions on behalf of other users.
For example, consider a Fastapi endpoint that receives a third-party event and uses an API key to authorize the caller. If the endpoint does not validate the event source beyond the key, and if the key is inadvertently exposed in logs or error messages, an attacker can replay requests or send malicious payloads. This becomes especially risky when the webhook triggers privileged operations such as changing user roles, initiating payouts, or modifying sensitive records, since the API key alone may grant sufficient authority to perform those actions.
Additionally, if your API definition (OpenAPI/Swagger) describes the webhook route as requiring only an API key, and the runtime behavior does not enforce stricter checks, there is a mismatch between documented authentication and actual authorization. middleBrick’s OpenAPI/Swagger spec analysis can detect such inconsistencies by cross-referencing spec definitions with runtime findings, highlighting where authentication schemes like API keys are underspecified for webhook receivers.
Another vector specific to webhooks is SSRF via the webhook payload. If the Fastapi endpoint forwards data from the webhook request to internal services without proper validation, an attacker who controls the webhook body might induce the server to probe internal endpoints. Since the request includes a valid API key, the forwarded request may succeed, leading to unauthorized internal access. The 12 parallel security checks in middleBrick include SSRF and Input Validation, which can surface such risks when scanning the webhook endpoint.
Finally, because webhooks are often invoked asynchronously, it can be difficult to correlate malicious events with a specific source. If API keys are rotated infrequently and there is no per-event signer or nonce, replay attacks become more feasible. middleBrick’s active probe set includes patterns that test for missing idempotency controls and missing origin checks, which are common contributors to webhook abuse in API designs that rely solely on API keys.
Api Keys-Specific Remediation in Fastapi — concrete code fixes
To reduce webhook abuse risk when using API keys in Fastapi, combine strict inbound validation with secure key handling and explicit authorization checks. Below are concrete, working examples that demonstrate these practices.
1. Validate the key on every request using a dependency
Define a reusable dependency that checks the incoming header against a set of valid keys and fails fast if the key is missing or invalid.
from fastapi import Depends, FastAPI, Header, HTTPException, status
app = FastAPI()
VALID_API_KEYS = {"s3cr3t-k3y-abc", "s3cr3t-k3y-xyz"}
def verify_api_key(x_api_key: str = Header(...)):
if x_api_key not in VALID_API_KEYS:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API key",
)
return x_api_key
@app.post("/webhook/example")
def webhook_handler(key: str = Depends(verify_api_key)):
return {"status": "ok", "key_used": key}
2. Use HTTPS and reject insecure requests
Ensure the endpoint only accepts HTTPS. While Fastapi does not enforce transport itself, you can add a startup check or middleware that rejects requests that are not using TLS, reducing the risk of key interception.
3. Add per-event signatures in addition to API keys
For webhooks, do not rely on the API key alone. Require a signature header derived from the payload and a shared secret. This prevents tampering and replay even if the API key is exposed.
import hashlib
import hmac
from fastapi import Request, HTTPException, status
def verify_signature(request: Request, shared_secret: str) -> bool:
signature_header = request.headers.get("X-Signature-256")
if not signature_header:
return False
body = request.body()
computed = hmac.new(shared_secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={computed}", signature_header)
@app.post("/webhook/signed")
def signed_webhook(request: Request):
if not verify_signature(request, "my-shared-secret"):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid signature")
return {"status": "ok"}
4. Scope keys by origin and enforce CORS strictly
Store metadata about each API key, such as allowed IP ranges or referrer origins, and validate these properties on each request. Do not rely on CORS alone, but use it as an additional layer.
from fastapi import Depends
from fastapi.security import HTTP_ProxyHeaders
def verify_origin_and_key(x_api_key: str = Header(...), x_forwarded_for: str = Header(None)):
# Example: ensure key is tied to an expected source IP range (simplified)
if x_api_key not in VALID_API_KEYS:
raise HTTPException(status_code=401, detail="Unauthorized")
# Add origin or IP checks here as needed
return True
@app.post("/webhook/restricted")
def restricted_webhook(ok: bool = Depends(verify_origin_and_key)):
return {"status": "ok"}
5. Rotate keys and avoid logging them
Treat API keys as credentials. Rotate them regularly, avoid printing them in logs, and store them securely using environment variables or a secrets manager. In Fastapi, read keys from environment variables rather than hardcoding them.
import os
from fastapi import FastAPI
app = FastAPI()
API_KEY = os.getenv("API_KEY")
if not API_KEY:
raise RuntimeError("API_KEY environment variable is required")
6. Apply principle of least privilege
Ensure the API key used by the webhook has only the permissions required to perform the intended action. If the key can read and write, scope it to write-only where possible and validate each incoming operation against an authorization model.