A07: Fallos de Autenticacion (Blog)
¿Que es un fallo de autenticacion?
Los Fallos de Autenticacion ocurren cuando el login, el manejo de sesiones, el reset de passwords o la autenticacion por tokens se implementan/configuran de forma que un atacante pueda evadir controles o mantener acceso.
Los starter kits de Laravel 12 aplican throttling por defecto, las sesiones deben regenerar el ID para prevenir session fixation, Fortify soporta 2FA, y el password broker de Laravel maneja flujos de reset. Sanctum soporta abilities en tokens y es recomendado para muchos casos first-party SPA/API.
Fallo tipico en Laravel
- Throttling debil en login.
- Sin MFA.
- Flujo de reset de password mal disenado.
- Sesiones de larga vida con mala invalidacion.
- Mezclar auth de SPA/API incorrectamente.
Impacto
- Toma de cuentas por credential stuffing / fuerza bruta.
- Sesiones persistentes despues de logout o cambio de password.
- Abuso de tokens de reset y takeover.
- Escalamiento cuando se mezclan tokens/sesiones mal.
Remediacion en Laravel 12
- Usa Fortify o starter kits en lugar de inventar auth.
- Habilita 2FA.
- Exige confirmacion de password para cambios sensibles.
- Regenera sesiones al login e invalida en logout/cambio de password.
- Para APIs, usa abilities de Sanctum (o Passport solo si necesitas OAuth2 de verdad).
- Separa correctamente auth por sesion (browser) de auth por token.
Para un blog:
- Lectura de posts/comentarios debe ser publica (sin auth).
- Creacion de comentarios debe requerir autenticacion (
authmiddleware / Form Request authorize). - Creacion de posts debe requerir autorizacion mas fuerte (rol/permisos).
Arreglo concreto
Endurecimiento de sesion (evita fixation, mejora invalidacion):
$request->session()->regenerate(); // En logout// Auth::logout();// $request->session()->invalidate();// $request->session()->regenerateToken();
Fortify (habilita 2FA a nivel framework):
// config/fortify.php use Laravel\Fortify\Features; 'features' => [ // ... Features::twoFactorAuthentication(),],
Passwords (usa el password broker, no tokens custom):
use Illuminate\Support\Facades\Password; Password::sendResetLink(['email' => $request->string('email')->toString()]);
Sanctum (abilities en tokens para APIs):
$token = $user->createToken('api', ['orders:read', 'orders:write'])->plainTextToken;
Enfoque de patron de diseno
Separa auth por sesion (browser) y auth por token (API) como puntos de entrada distintos del caso de uso. Usa Actions como limites y evita mezclar ambos modelos en el mismo controller.
interface AuthContext{ public function user(): User; public function type(): string; // 'session' | 'token'} final class SessionAuthContext implements AuthContext{ public function __construct(private User $user) {} public function user(): User { return $this->user; } public function type(): string { return 'session'; }} final class TokenAuthContext implements AuthContext{ public function __construct(private User $user) {} public function user(): User { return $this->user; } public function type(): string { return 'token'; }} final class ChangeEmailAction{ public function handle(AuthContext $auth, string $newEmail): void { // invariante: exigir confirmacion de password en flujos de sesion if ($auth->type() === 'session') { // p.ej. middleware('password.confirm') a nivel de ruta } $auth->user()->forceFill(['email' => $newEmail])->save(); }} // Ejemplos de uso // Web route/controller (auth por sesion)public function updateEmail(Request $request, ChangeEmailAction $action){ $request->validate(['email' => ['required', 'email']]); $action->handle( new SessionAuthContext($request->user()), $request->string('email')->toString(), ); return back();} // API route/controller (auth por token)public function updateEmailApi(Request $request, ChangeEmailAction $action){ $request->validate(['email' => ['required', 'email']]); $action->handle( new TokenAuthContext($request->user()), $request->string('email')->toString(), ); return response()->noContent();}