php elephant sticker
Photo by RealToughCandy.com on Pexels.com

[Guía Práctica] Fortalecimiento de la Seguridad y Fiabilidad en Laravel

Autenticación/Autorización, 2FA/WebAuthn, CSP/Headers, Entrada/Archivos, Recuperación MFA, Registros de Auditoría, Multi-Tenant, Diseño Accesible y Seguro

Qué Aprenderás (Destacados)

  • Cómo estructurar de forma segura la autenticación/autorización en Laravel (Fortify/Sanctum/Policies)
  • 2FA, WebAuthn (sin contraseña), códigos de recuperación, aprobación de dispositivos y UX accesible
  • Cómo manejar vulnerabilidades reales en validación, carga de archivos, SSRF, inyección de comandos
  • Valores concretos y trampas de cabeceras seguras como HTTPS, HSTS, CSP, Permissions-Policy y Referrer-Policy
  • Operación segura de URLs firmadas, webhooks firmados, idempotencia, colas/jobs, protección de secretos y rotación de claves
  • Separación multi-tenant, registros de auditoría, enmascaramiento de PII, respaldo/restauración y resiliencia
  • Una lista de verificación completa, errores comunes, código de ejemplo y diseño de errores que convive con la accesibilidad

Lectores Objetivo (¿Quién obtiene más valor?)

  • Ingenieros Laravel principiantes–intermedios que quieran cubrir a fondo la seguridad básica
  • Líderes técnicos / PMs que quieran definir lineamientos estándar de seguridad para SaaS / plataformas internas
  • Especialistas en CS / QA / accesibilidad que quieran que MFA y mensajes de error sean comprensibles para todos

Nivel de Accesibilidad: ★★★★★

Explicamos con detalle textos y flujos para login/2FA/bloqueo/recuperación, lectores de pantalla (role="status" / alert), estados independientes del color, operación por teclado, alternativas a CAPTCHAs de imagen y diseños que respeten prefers-reduced-motion.


1. Principio: La Seguridad y la Usabilidad Pueden Coexistir

La seguridad depende de los activos que proteges × tu superficie de ataque × tus hábitos operativos. Laravel incluye protección CSRF, protección XSS, cifrado y autorización, pero hay puntos donde operaciones tiende a abrir huecos: archivos, webhooks, recuperación MFA, logs/auditoría, etc. Y si los flujos de inicio de sesión y 2FA son confusos, la gente los evita.

Este artículo muestra métodos prácticos para mejorar tanto la seguridad como la usabilidad.


2. Autenticación: Fortify/Sanctum y Sesiones Endurecidas

2.1 Política de Contraseñas

  • 12+ caracteres, enfocándose en longitud sobre complejidad
  • No prohibir pegar (soporte para gestores de contraseñas)
  • Usar Hash::make() (bcrypt/argon2id). Re-hash con needsRehash():
if (Hash::needsRehash($user->password)) {
    $user->password = Hash::make($plain);
    $user->save();
}

2.2 Protección contra Fijación de Sesión

  • Regenerar sesión al iniciar sesión (Fortify lo hace por defecto)
  • Guardar sesiones del lado servidor (Redis, etc.) y habilitar SameSite=Lax + Secure / HttpOnly:
// config/session.php
'driver' => 'redis',
'secure' => true,
'http_only' => true,
'same_site' => 'lax',

2.3 Sanctum y Alcances (Abilities)

  • SPA: basado en cookies (stateful)
  • API/móvil: tokens de acceso personal + privilegio mínimo
  • Para operaciones críticas, requerir verificación adicional (re-auth / 2FA)
$token = $user->createToken('cli', ['orders:read','orders:create'])->plainTextToken;
abort_unless($request->user()->tokenCan('orders:create'), 403);

3. Autenticación Multifactor (2FA) y Passwordless (WebAuthn)

3.1 TOTP (2FA basado en App)

  • Activar TOTP y códigos de recuperación con Fortify
  • Pantallas accesibles: label, aria-describedby, inputmode="numeric", role="alert"
<label for="otp">Código de 6 dígitos</label>
<input id="otp" name="code" inputmode="numeric" autocomplete="one-time-code"
       aria-describedby="otp-help" class="w-40">
<p id="otp-help" class="text-sm text-gray-600">Ingresa el código de 6 dígitos de tu app autenticadora.</p>
  • Cuando fallen varios intentos, mostrar mensajes suaves y Retry-After sin revelar demasiado el estado de bloqueo

3.2 Códigos de Recuperación y Opciones de Respaldo

  • Al activar 2FA, pedir guardar los códigos inmediatamente
  • Proveer claramente el canal de soporte
  • Guardar códigos con hash y anularlos tras uso

3.3 WebAuthn (Biometría / Llaves de Seguridad)

  • Puede usarse como passwordless o segundo factor
  • UI debe soportar uso completo por teclado y lectores de pantalla, con explicaciones breves
const cred = await navigator.credentials.create({ publicKey: options });

4. Autorización: Gates/Policies y Privilegio Mínimo

public function update(User $user, Order $order): bool
{
    return $order->user_id === $user->id || $user->can('orders:update:any');
}

5. Entrada/Salida Segura: XSS, CSRF, SQLi, Plantillas

5.1 XSS

  • {{ }} escapa por defecto
  • Sanitizar Markdown/rich text con lista blanca

5.2 CSRF

  • Siempre usar @csrf
  • APIs con cookies: sanctum/csrf-cookie
  • APIs con tokens: excluir de CSRF y validar headers

5.3 SQL Injection

$allowed = ['created_at','score'];
$col = in_array($req->get('sort'), $allowed, true) ? $req->get('sort') : 'created_at';
$query->orderBy($col, 'desc');

5.4 Inyección en Plantillas

  • No incrustar entrada de usuario en directivas Blade o JS inline

6. Archivos/Imágenes Seguros: MIME, EXIF, Escaneo, Entrega

6.1 Validación

$request->validate([
  'file' => ['required','file','mimetypes:image/jpeg,image/png,application/pdf','max:8192'],
]);

6.2 EXIF

  • Eliminar EXIF de imágenes

6.3 Escaneo

  • Usar ClamAV u otros async

6.4 URLs Firmadas

  • Archivos en storage privado + temporaryUrl()

7. Integraciones Externas: HTTP Client, Webhooks, SSRF

7.1 Webhooks Salientes

$payload = json_encode($data);
$sig = hash_hmac('sha256', $payload, config('app.webhook_secret'));
Http::withHeaders(['X-Signature'=>$sig, 'Idempotency-Key'=>$uuid])->post($url, $data);

7.2 Webhooks Entrantes

  • Validar firma y replay
  • Procesar async con idempotencia

7.3 SSRF

  • Tiempo de espera, bloquear IPs internas

8. Headers / HTTPS / CSP

return response($html, 200, [
  'Content-Security-Policy' => "default-src 'self'; img-src 'self' data:; script-src 'self' 'nonce-{$nonce}'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'",
  'X-Frame-Options' => 'DENY',
  'X-Content-Type-Options' => 'nosniff',
  'Referrer-Policy' => 'strict-origin-when-cross-origin',
  'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
]);

9. URLs Firmadas y Detección de Abuso

$url = URL::temporarySignedRoute('reports.show', now()->addMinutes(30), ['id'=>$report->id]);

10. Jobs/Queues Seguras

public function middleware(): array {
  return [ new \Illuminate\Queue\Middleware\RateLimited('external') ];
}

11. Multi-Tenant

protected static function booted() {
  static::addGlobalScope('tenant', fn($q)=>$q->where('tenant_id', tenant()->id()));
}

12. Logging / Auditoría / Privacidad

Log::info('order.created', [
  'request_id' => request()->header('X-Request-Id'),
  'order_id' => $order->id,
  'user_id' => auth()->id(),
  'amount' => $order->amount,
]);

13. Secretos, Gestión de Claves, Cifrado

  • .env real solo en producción
  • Cifrado de campos y backups
  • Rotación con periodos superpuestos

14. Diseño de Errores y Accesibilidad

<div role="alert" class="mb-2">El código es incorrecto. Inténtalo nuevamente.</div>
<p id="next" class="text-sm">Usa un código de recuperación o contacta soporte.</p>

15. Snippets Representativos

15.1 Rate Limiting de Login

RateLimiter::for('login', function ($request) {
  $key = 'login:'.strtolower($request->input('email')).'|'.$request->ip();
  return [Limit::perMinute(5)->by($key)];
});

15.2 Verificación de Webhook

$payload = $request->getContent();
$signature = $request->header('X-Signature');
$calc = hash_hmac('sha256', $payload, config('services.partner.secret'));
abort_unless(hash_equals($calc, $signature), 401);

15.3 Pasando Nonce CSP

$request->attributes->set('csp_nonce', bin2hex(random_bytes(16)));
<script nonce="{{ request()->attributes->get('csp_nonce') }}">/* … */</script>

16. Errores Comunes

  • Flujo 2FA complejo → siempre códigos de recuperación
  • Solo CAPTCHA de imagen → dar alternativas
  • CSP estricta de golpe → usar Report-Only
  • Webhooks sin verificación → firma + replay + idempotencia
  • Archivos en public/ → storage privado
  • Logs con PII → enmascarar

17. Checklist

(Se mantiene idéntico, traducido)

Auth / Autorización

  • [ ] Política de contraseñas (longitud, pegado permitido, rehash)
  • [ ] 2FA (TOTP/WebAuthn), códigos de recuperación, re-auth
  • [ ] Cookies seguras, mitigación de fijación de sesión
  • [ ] Privilegio mínimo

Entrada / Salida

  • [ ] XSS: auto-escape, sanitización
  • [ ] CSRF: @csrf, políticas claras API
  • [ ] SQLi: builder, listas blancas

Archivos / Integraciones

  • [ ] MIME, EXIF, escaneo
  • [ ] URLs firmadas, storage privado
  • [ ] Webhooks firmados, defensa SSRF, timeouts

Headers / Transporte

  • [ ] HTTPS/HSTS, atributos cookie
  • [ ] CSP, X-CTO, Frame-Options, Referrer, Permissions-Policy

Colas / Operaciones

  • [ ] Idempotencia, locks, rate limiters
  • [ ] Monitor & DLQ

Multi-Tenant / Datos

  • [ ] Límites por tenant
  • [ ] Separación de storage y cache/sesión

Logs / Secretos

  • [ ] Logs estructurados, PII enmascarada
  • [ ] Gestión de claves, backups cifrados, rotación

Accesibilidad

  • [ ] Pantallas Auth/2FA accesibles
  • [ ] Alternativas a CAPTCHAs
  • [ ] Manejo de foco correcto

18. Cierre

(Se mantiene idéntico, traducido)

  • Complementa funciones nativas con 2FA/WebAuthn, URLs firmadas, headers seguros y jobs idempotentes.
  • Repara puntos ciegos: archivos, webhooks, límites multi-tenant, secretos y logging.
  • Haz que los flujos Auth/2FA sean accesibles y fáciles de entender.
  • La seguridad es un ciclo continuo: auditar → medir → mejorar.
  • Usa esta guía para crear tus lineamientos internos de seguridad.

Referencias

(Traducción de textos; enlaces intactos)

por greeden

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)