← OWASP Top 10:2025

A08:2025

Software or Data Integrity Failures

Trusting code or data without checking it came from where you think: unverified webhooks, unsafe deserialization, CI that pulls unpinned steps, auto-updates without signatures.

What it is

Integrity failures are about trusting something you did not verify. A webhook handler that processes any POST that looks like a payment event. A deserializer that turns untrusted bytes into objects, and therefore into code. A CI pipeline that runs a third-party step at the latest tag. An update mechanism with no signature. The common thread: the app assumes the input is what it claims to be.

It holds at eighth in 2025. The supply chain half of the old definition moved to A03; what remains is the application's own trust decisions.

How it shows up in AI-generated code

The AI-built version is almost always the webhook. Ask a tool to handle Stripe or GitHub webhooks and it will parse the JSON and act on it. Verifying the signature is a separate step with a separate secret, and unless the prompt mentioned it, it is missing. That means anyone who can find the URL can mark an order as paid. In Python apps the second pattern is pickle.loads or yaml.load on data from a request or a file, both of which execute code.

We also see timing-unsafe signature comparisons where verification does exist: comparing the HMAC with == leaks it byte by byte. Prbl's rules cover the deserialization and comparison cases; the webhook guide covers the rest.

Example: A webhook that believes the request

The pattern
// POST /api/webhooks/stripe
const event = JSON.parse(await req.text());
if (event.type === "checkout.session.completed") await markPaid(event.data.object.id);
The fix
const sig = req.headers.get("stripe-signature")!;
const raw = await req.text();
let event;
try {
  event = stripe.webhooks.constructEvent(raw, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch { return new Response("Bad signature", { status: 400 }); }
if (event.type === "checkout.session.completed") await markPaid(event.data.object.id);

Verify against the raw body, not a re-serialised one, and use the provider's helper so the comparison is constant-time and the timestamp is checked for replay.

How to find it

  • List every webhook route. For each, find the signature check. If there is none, that is the finding.
  • Search for pickle.loads, yaml.load without SafeLoader, unserialize, and Java ObjectInputStream on anything from outside.
  • Check CI workflows for third-party actions referenced by a mutable tag instead of a commit SHA.
  • Search for == or === comparing signatures or HMACs.
  • Prbl rules PRBL-R002 and the deserialization fix guides cover the code side; the webhook guide has the per-provider checklist.

How to fix it

  • Verify every webhook with the provider's secret and helper, on the raw body, and reject on failure.
  • Never deserialize untrusted data with a format that can execute code. Use JSON, or a safe loader.
  • Pin CI actions to commit SHAs.
  • Compare secrets with a constant-time function.
  • Sign anything you ship to be auto-updated, and verify it on the receiving side.

What Prbl checks for this category

PRBL-R002 Timing-unsafe comparisonDeserialization fix guidesWebhook verification guide

Run a free scan on a public repo or a live URL. Findings link to the fix guides below.

Fix guides for this category

Go deeper

Common questions

Why is an unverified webhook so serious?

Because the webhook is usually the thing that grants value: marks an order paid, upgrades a plan, provisions access. Without a signature check, whoever finds the URL can send that event themselves. Stripe, GitHub, Twilio and every serious provider sign their webhooks; the handler just has to check.

Is JSON.parse an integrity risk?

No. JSON has no code execution; the worst it does is produce a large object. The dangerous formats are the ones that reconstruct arbitrary objects: Python pickle, unsafe YAML, PHP unserialize, Java native serialization.

What moved to A03 in 2025?

The dependency and build-pipeline half. This category used to include vulnerable components; those now live in Software Supply Chain Failures. A08 keeps the app's own trust decisions: webhooks, deserialization, updates, and CI steps it chooses to run.

Is this category already in something you shipped? Scan a live URL or a public repo free, no account.

Scan my app →
A08:2025 Software or Data Integrity Failures Explained, With Webhook and Deserialization Examples | Prbl