Skip Content

A06: Diseno Inseguro (Blog)

¿Que es el diseno inseguro?

El Diseno Inseguro ocurre cuando las reglas de seguridad no existen a nivel de caso de uso. El codigo puede estar "correcto" y aun asi el flujo ser explotable porque no se disenan abusos, restricciones e invariantes.

Fallo tipico en Laravel

  • Construir features primero, seguridad despues.
  • Sin casos de abuso.
  • Sin restricciones de workflow para operaciones criticas (publicar posts).
  • Sin rate limits/moderacion para comentarios.
  • Sin aprobacion o doble control para acciones peligrosas.

Impacto

  • Abuso de logica de negocio (el atacante usa tu flujo valido en tu contra).
  • Exposicion de borradores/contenido privado si no modelas visibilidad.
  • Cambios criticos sin control (pagos, roles, transferencias de propiedad).
  • Sin rastro forense cuando falta auditoria.

Remediacion en Laravel 12

  • Disena reglas de seguridad antes de codificar endpoints.
  • Modela abusos en el propio caso de uso.
  • Usa Actions como limites de caso de uso.
  • Para Actions criticas, exige invariantes:
    • actor autorizado
    • transicion de estado permitida
    • evento de auditoria emitido
  • Introduce rate limits, idempotencia, flujos de aprobacion y auditoria inmutable cuando aplique.

Arreglo concreto

Trata autorizacion + invariantes como las primeras lineas de tu Action (publicar posts):

use Illuminate\Support\Facades\Gate;
 
final class PublishPostAction
{
public function handle(User $actor, Post $post): void
{
// invariante: actor autorizado a escribir posts
Gate::authorize('update', $post);
 
// invariante: transicion de estado permitida
abort_unless($post->status === 'draft', 422);
 
// cambio de estado
$post->update(['status' => 'published']);
 
// invariante: evento de auditoria emitido
// event(new PostPublished($post->id, $actor->id));
}
}

Enfoque de patron de diseno

Usa un Action como limite y refuerza invariantes con una cadena pequena tipo pipeline / decorador. Asi mantienes checks criticos consistentes entre casos de uso (auth, ownership/visibilidad, transicion de estado, auditoria).

interface Invariant
{
public function check(array $context): void;
}
 
final class EnsureDraftStatus implements Invariant
{
public function check(array $context): void
{
/** @var Post $post */
$post = $context['post'];
 
abort_unless($post->status === 'draft', 422);
}
}
 
final class InvariantRunner
{
/** @param array<int, Invariant> $invariants */
public function __construct(private array $invariants) {}
 
public function run(array $context): void
{
foreach ($this->invariants as $invariant) {
$invariant->check($context);
}
}
}
 
// Uso dentro de un Action
$runner = new InvariantRunner([
new EnsureDraftStatus(),
// new EnsureAuditEnabled(),
]);
 
$runner->run([
'actor' => $actor,
'post' => $post,
]);
 
// continuar con el caso de uso

Referencias