Skip Content

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_id for 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 user
Comment::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,
]);

References