A08: Fallos de Integridad de Software o Datos (Blog)
¿Que es un fallo de integridad de software o datos?
Estos fallos ocurren cuando la aplicacion confia en datos o artefactos que pueden ser manipulados. Incluye webhooks, estado del cliente, payloads serializados, imports y artefactos de CI/CD.
Fallo tipico en Laravel
- Confiar en webhooks sin verificar firmas.
- Deserializacion insegura.
- Confiar ciegamente en datos del cliente (ej.
user_iden comentarios). - Importar archivos/datos sin controles de integridad.
- Artefactos de CI/CD sin verificacion.
Impacto
- Fraude, escalamiento de privilegios o acciones cross-user.
- Deploys comprometidos por artefactos envenenados.
- Corrupcion de datos por imports inseguros.
Remediacion en Laravel 12
- Verifica firmas de webhooks (Stripe, Paddle, GitHub, etc.).
- No uses
unserializecon payloads no confiables. - Recalcula totales/precios sensibles del lado del servidor.
- Trata todo estado del cliente como hostil.
- Firma builds, protege pipelines de deploy y limita quien puede promover artefactos.
- Valida archivos subidos y escanea cuando aplique.
Arreglo concreto
Nunca confies en ownership enviado por el cliente.
En comentarios: siempre toma user_id desde la sesion autenticada.
// Mal: confiar en el owner enviado por el cliente// Comment::create([// 'post_id' => $request->input('post_id'),// 'user_id' => $request->input('user_id'),// 'body' => $request->input('body'),// ]); // Bien: derivar ownership del usuario autenticadoComment::create([ 'post_id' => $post->id, 'user_id' => $request->user()->id, 'body' => $request->string('body')->toString(),]);
Enfoque de patron de diseno
Usa Template Method o un Pipeline para imports para que validacion, checks de integridad y autorizacion no sean pasos opcionales. Haz que el pipeline sea el unico punto de entrada publico para 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; }} // Pasos: authorize -> validate -> verify integrity -> persist -> audit
Ejemplo de pasos (integridad primero, luego persistir):
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('Fallo chequeo de integridad'); } 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,]);