A08: Software or Data Integrity Failures (Blog)
What is a software or data integrity failure?
Software or Data Integrity Failures happen when an application trusts data or artifacts that can be tampered with.
That includes webhooks, client-side state, serialized payloads, imports, and CI/CD build artifacts.
Typical Laravel failure (blog)
- Trusting webhooks without signature verification.
- Unsafe deserialization.
- Blind trust in client-side data (e.g. accepting
user_idfor comments). - Importing files/data without integrity controls.
- CI/CD artifacts not verified.
Impact
- Fraud, privilege escalation, or cross-user actions.
- Compromised deployments via poisoned artifacts.
- Data corruption from unsafe imports.
Laravel 12 remediation
- Verify webhook signatures for Stripe, Paddle, GitHub, etc.
- Do not unserialize untrusted payloads.
- Recompute sensitive totals/prices server-side.
- Treat all client state as hostile.
- Sign builds, protect deploy pipelines, restrict who can promote artifacts.
- Validate uploaded files and scan where needed.
Concrete fix
Never trust client-submitted ownership fields.
For comments: always take user_id from the authenticated session.
// Bad: trusting client-submitted owner// Comment::create([// 'post_id' => $request->input('post_id'),// 'user_id' => $request->input('user_id'),// 'body' => $request->input('body'),// ]); // Good: derive ownership from the authenticated userComment::create([ 'post_id' => $post->id, 'user_id' => $request->user()->id, 'body' => $request->string('body')->toString(),]);
Design pattern angle
Use Template Method or a Pipeline for import flows so validation, integrity checks, and authorization are not optional steps. Make the pipeline the only public entry point for imports.
final class ImportPipeline{ /** @param array<int, callable(array): array> $steps */ public function __construct(private array $steps) {} public function run(array $context): array { foreach ($this->steps as $step) { $context = $step($context); } return $context; }} // Steps: authorize -> validate -> verify integrity -> persist -> audit
Example steps (integrity first, then persist):
use Illuminate\Support\Facades\Gate; $pipeline = new ImportPipeline([ function (array $c): array { Gate::authorize('import', $c['actor']); return $c; }, function (array $c): array { validator($c['rows'], ['*.email' => 'required|email'])->validate(); return $c; }, function (array $c): array { if (!hash_equals($c['expected_sha256'], hash('sha256', $c['raw']))) { throw new \RuntimeException('Integrity check failed'); } return $c; }, function (array $c): array { ImportJob::dispatch($c['rows']); return $c; },]); $pipeline->run([ 'actor' => $request->user(), 'raw' => $rawFileContents, 'expected_sha256' => $request->string('sha256')->toString(), 'rows' => $rows,]);