Skip to content
CVE-2026-21894: n8n Missing Stripe-Signature Verification Allows Forged Webhooks

CVE-2026-21894: n8n Missing Stripe-Signature Verification Allows Forged Webhooks

Grayscale portrait of a young man looking left, wearing a striped lanyard, with an olive green dot pattern background.Artemiy Malyshau· Co-founder & CTO3 min read

Key takeaways

  • An authentication bypass in the StripeTrigger node allows any party who knows the webhook URL to forge arbitrary Stripe webhooks without knowing the Stripe webhook signing secret.
  • The StripeTrigger webhook handler never checks that incoming requests really come from Stripe.
  • cloud tenants) can trigger that workflow by sending arbitrary JSON that has a matching type field, without knowing the Stripe webhook signing secret.

Advisory#

Description#

An authentication bypass in the StripeTrigger node allows any party who knows the webhook URL to forge arbitrary Stripe webhooks without knowing the Stripe webhook signing secret.

The node stores the Stripe webhook secret when creating the webhook endpoint, but the webhook() handler never verifies the Stripe-Signature header or the request body. It only checks that the type field of the incoming JSON matches one of the configured event types, then passes the entire unverified body directly into the workflow.

In contrast, other n8n webhook triggers like Slack, WooCommerce, and HelpScout verify provider signatures/HMAC over the raw request body and reject on mismatch. StripeTrigger stores the secret but performs no verification.

This design means that any unauthenticated HTTP client (not just Stripe) can send POST requests to the publicly exposed webhook URL and cause workflows to execute as if a legitimate Stripe event occurred, enabling payment forgery, subscription manipulation and other downstream attacks depending on how workflows are built.

Source - Sink Analysis#

The StripeTrigger webhook handler never checks that incoming requests really come from Stripe.

In StripeTrigger.node.ts, the webhook() function:

  1. Reads the JSON body from the request
  2. Checks that bodyData.type matches one of the configured event types
  3. Passes the full req.body into the workflow

What it does NOT do:

  • Read the Stripe-Signature header
  • Use the stored webhook secret
  • Verify any HMAC or signature over the raw body

The create method stores the Stripe webhook secret when the endpoint is created, but that value is never used in webhook().

Comparison with secure implementations: Other trigger nodes (like WooCommerce and HelpScout) correctly compute an HMAC of req.rawBody with their secret and compare it to the provider’s signature header, rejecting invalid or missing signatures. Because StripeTrigger skips this step entirely, any attacker who knows the webhook URL can send a plain HTTP POST with a JSON body that has a matching type (e.g. payment_intent.succeeded), and n8n will treat it as a genuine Stripe event.

Proof of Concept#

On a deployed n8n cloud tenant (e.g. https://<tenant>.app.n8n.cloud):

1. Create new workflow in n8n cloud workspace

2. Add a Stripe Trigger node:

  • Set Events to payment_intent.succeeded (or *)
  • Connect it to any simple node to easily observe execution

3. Activate the workflow

4. Run the following with the Production Webhook URL:

<code class="hljs language-bash">WEBHOOK_URL=<span class="hljs-string">"https://<tenant>.app.n8n.cloud/webhook/<uuid>/webhook"</span>

<span class="hljs-comment"># Forged Stripe webhook with a fake signature</span>
curl -v -X POST <span class="hljs-string">"<span class="hljs-variable">$WEBHOOK_URL</span>"</span> \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -H <span class="hljs-string">"Stripe-Signature: FORGED"</span> \
  -d <span class="hljs-string">'{
    "type": "payment_intent.succeeded",
    "data": {
      "object": {
        "id": "pi_FORGED",
        "amount": 999999,
        "currency": "usd",
        "status": "succeeded"
      }
    }
  }'</span>
</code>

5. Observe the result:

  • The HTTP response is 200 OK
  • The n8n UI shows the successful run under Executions
  • The input data contains the forged payload, even though the Stripe-Signature is invalid and the IDs are fabricated

Impact#

This is an authentication bypass on the Stripe webhook endpoint: any attacker who learns the StripeTrigger webhook URL for a given n8n workflow (including on *.app.n8n.cloud tenants) can trigger that workflow by sending arbitrary JSON that has a matching type field, without knowing the Stripe webhook signing secret.

All n8n users who use the StripeTrigger node in active workflows are impacted, because those workflows will trust and act on forged payment or subscription events as if they came from Stripe.

In practice this can be abused to:

  • Fake successful payments: granting free access or shipping goods without actual payment
  • Cancel or modify subscriptions: denial of service for legitimate customers
  • Business-logic manipulation: depending on how the workflow uses the incoming data (Code nodes, HTTP requests, database writes, templated emails/web pages), attackers can inject malicious data into downstream systems
Grayscale portrait of a young man looking left, wearing a striped lanyard, with an olive green dot pattern background.

Artemiy Malyshau

Co-founder & CTO

Artemiy served in an elite unit of the Austrian Cyber Forces, defending national infrastructure He was then the first employee at a government-backed cybersecurity research group, where he led security projects for Interpol and national governments. At Gecko he builds the platform trusted to sit inside Fortune 500 codebases, and holds it to the standard those governments taught him.

Frequently asked questions

Related content

The latest news, technologies, and resources from our team.

Subscribe to the Gecko Security newsletter

Occasional updates, new content, and insights. No spam; unsubscribe anytime.

We use your email only to send you our newsletter. See our privacy policy for how we handle your data. You can unsubscribe at any time.