The most interesting security story on r/netsec today is not a Stripe breach.

It is a reminder that a lot of web apps still treat a payment-shaped POST request as if it were proof of payment.

A fresh scanning write-up from Security Scanner says its probe sent a fake checkout.session.completed event to roughly 6,000 public web apps and got 200, 201, or 202 responses from 1,542 of them, all without a Stripe-Signature header. That does not prove all 1,542 apps would hand out paid plans or ship unpaid orders. It does show something more basic and still ugly: a large number of public webhook endpoints do not fail closed when the cryptographic proof is missing.

That is why the thread got hot. The headline number is dramatic, but the real story is older and more structural. Too many teams wire up the business logic around billing events first, then leave authenticity checks as a cleanup task that never quite gets done.

What is actually verified

The primary source is Security Scanner's post, "We probed 6,000 web apps for Stripe webhook signature checks. 1,542 don't bother.". The methodology it describes is simple on purpose: send a minimal fake Stripe-style event to common webhook paths and count endpoints that accept it without Stripe's signature header.

Two important parts of that post are easy to ground against Stripe's own documentation.

First, Stripe does expect servers to verify provenance, not just parse JSON. In Stripe's docs for resolving webhook signature verification errors, the company says to use the Stripe-Signature header and call constructEvent() with three inputs: the raw request body, the signature, and the endpoint secret.

Second, Stripe is explicit that the raw request body matters. The signature is computed over the original payload bytes. If middleware parses and rewrites the body before verification, the check can fail even when the event is legitimate. That detail matters because it explains why webhook security keeps turning into a framework trap instead of a one-line box everyone checks.

That trap is also public and old. Stripe's Node SDK issue tracker has years of reports from developers struggling with exactly this problem:

  • Issue #341: bodyParser.json() breaks the documented webhook pattern when used globally in Express.
  • Issue #356: AWS API Gateway and Lambda payload handling changes the body enough to break verification.
  • Issue #1294: a Next.js user spends half a day fighting raw-body handling and the same signature error.
  • Issue #1886: another developer reports that even the Express example still leads to the familiar "No signatures found matching the expected signature for payload" failure.

None of those GitHub issues prove Security Scanner's 1,542 count. They do corroborate the broader claim that Stripe webhook verification is a recurring operational footgun across popular stacks.

Why this is more interesting than one scary scan

The easy version of this story is "developers forgot a security check."

The more useful version is harsher.

Webhook integrations tend to be built in the wrong order. Teams usually start with the happy path: receive event, find account, mark subscription active, return 200. Only after that works do they circle back to authenticity, replay resistance, event-type allowlists, and route-specific raw-body handling. By then the code is already in production, already tied into billing state, and already surrounded by middleware that makes the correct fix slightly annoying.

That is how you get an endpoint that is functionally part of your payment system but still behaves like a normal JSON API route.

The Security Scanner post argues that code assistants make this worse, because generated examples often produce a route that parses an event and calls the upgrade function, while signature verification stays separate from the main flow. I did not independently test that claim here, so treat it as the author's interpretation, not a verified measurement. Even without the AI angle, the underlying pattern is credible. The GitHub issue trail is full of developers who were clearly trying to do the right thing and still got pushed into awkward framework-specific fixes.

The number is real. The scope is still uncertain.

This is where the wording matters.

Security Scanner's count is best read as an upper bound on risky endpoints, not as a proven count of fully exploitable SaaS products.

The probe counted any 200, 201, or 202 response to a fake unsigned event. Some of those handlers may do nothing important. Some may log the event and return success. Some may reject internally while still answering with a success code. The write-up itself acknowledges that point.

So the strongest verified claim is not "1,542 apps can all be robbed with one curl command." The strongest verified claim is that 1,542 publicly reachable endpoints accepted an unsigned Stripe-style event in a way that did not visibly fail closed.

That is still a serious result. Payment webhook routes are not supposed to be permissive about provenance.

Why the Reddit signal matters

The r/netsec thread was new and still light on detailed comments when I checked it, so there was not much crowd forensics to quote yet. But it rose quickly because the story hits a familiar nerve for security engineers: the most durable bugs are often not exotic memory corruption bugs or clever auth bypasses. They are trust-boundary mistakes hiding inside normal application plumbing.

That is the real angle here.

This is not mainly a Stripe story. It is a story about what happens when an application treats a JSON shape as evidence. Once your code sees checkout.session.completed and jumps straight to side effects, the rest of the security model depends on whether you proved who sent it. If that proof is absent, the app is trusting syntax instead of origin.

What developers should take from this

If you run Stripe webhooks, the immediate lesson is boring but not optional.

Verify the signature before business logic. Use the raw body, not a parsed-and-reencoded copy. Put the webhook route on middleware that preserves the original payload. Fail closed when the signature is missing or invalid. Do not make a success response your default while you figure the rest out later.

A second lesson matters just as much: treat payment webhooks as a trust boundary, not as glue code. They may look like a small integration detail, but they sit on the path that turns external events into entitlements, order state, and money.

That is why this Reddit-hot post deserves more attention than the usual "1,500 apps vulnerable" scare line. The bug class is plain, old, and still everywhere.

Sources