Aplicacion Web de Ejemplo (Laravel 12)
Este repositorio es la aplicacion de ejemplo.
Los directorios vendor/ y node_modules/ no se documentan aqui a proposito.
Estructura del Proyecto (Alto Nivel)
app/ Example/ # Codigo de ejemplo A01-A10 (patrones seguros) Http/Middleware/bootstrap/config/public/resources/ docs/ # Documentacion Markdown renderizada como views views/routes/storage/tests/
OWASP 2025 A01-A10 (Mapa de Codigo)
A01: Control de Acceso Roto
Policies + autorizacion en Form Requests (lectura publica, escritura restringida):
Archivos:
app/Example/Policies/PostPolicy.php
app/Example/Http/Requests/UpdatePostRequest.php
app/Example/Policies/CommentPolicy.php
app/Example/Http/Requests/StoreCommentRequest.php
// app/Example/Policies/PostPolicy.php// Solo autores pueden crear posts.public function create(User $user): Response{ return (bool) $user->getAttribute('is_author') ? Response::allow() : Response::denyAsNotFound();}
// app/Example/Http/Requests/UpdatePostRequest.phppublic function authorize(): bool{ return $this->user()?->can('update', $this->route('post')) ?? false;}
// app/Example/Policies/CommentPolicy.php// Cualquier usuario autenticado puede crear un comentario.public function create(User $user): Response{ return $user->id > 0 ? Response::allow() : Response::denyAsNotFound();} // Solo el owner (o un admin) puede editar/eliminar.public function update(User $user, Comment $comment): Response{ $isAdmin = (bool) $user->getAttribute('is_admin'); return ($isAdmin || $user->id === $comment->user_id) ? Response::allow() : Response::denyAsNotFound();}
// app/Example/Http/Requests/StoreCommentRequest.phppublic function authorize(): bool{ // Requisito: solo usuarios autenticados pueden comentar. return $this->user() !== null;} public function rules(): array{ return [ 'body' => ['required', 'string', 'max:2000'], ];}
A02: Configuracion de Seguridad Incorrecta
Endurece sesion + CORS y mantenlo controlado por variables de entorno.
Archivos:
config/session.php
config/cors.php
// config/session.php'secure' => env('SESSION_SECURE_COOKIE', env('APP_ENV') === 'production'),'http_only' => env('SESSION_HTTP_ONLY', true),'same_site' => env('SESSION_SAME_SITE', 'lax'),
// config/cors.php'allowed_origins' => array_values(array_filter(array_map( trim(...), explode(',', (string) env('CORS_ALLOWED_ORIGINS', '')),))),'supports_credentials' => (bool) env('CORS_SUPPORTS_CREDENTIALS', false),
Baseline recomendado para .env (produccion):
APP_ENV=productionAPP_DEBUG=false SESSION_SECURE_COOKIE=trueSESSION_HTTP_ONLY=trueSESSION_SAME_SITE=lax # Ejemplo: limitar CORS a tus origenes realesCORS_ALLOWED_ORIGINS=https://app.ejemplo.com
A03: Fallas en la Cadena de Suministro de Software
Fija dependencias y auditarlas en CI.
Archivos:
composer.lock
composer.json (scripts)
Comandos:
composer auditcomposer outdated
A04: Fallas Criptograficas
Usa Hash para passwords y Crypt solo para valores que deban desencriptarse; rota llaves de forma segura.
Archivos:
app/Example/Crypto/LaravelCipher.php
// app/Example/Crypto/LaravelCipher.phpreturn Crypt::encryptString($plain);
Patron recomendado de rotacion en .env:
APP_KEY=base64:NUEVA_LLAVE_AQUIAPP_PREVIOUS_KEYS=base64:LLAVE_ANTIGUA_1,base64:LLAVE_ANTIGUA_2
A05: Inyeccion
Nunca dejes que input del usuario se convierta en identificadores SQL (columnas/order), SQL raw o comandos.
Archivos:
app/Example/Query/PostIndexQuery.php
$allowedSorts = ['created_at', 'title'];$sort = $request->string('sort')->toString();$sort = in_array($sort, $allowedSorts, true) ? $sort : 'created_at'; $direction = strtolower($request->string('direction')->toString());$direction = in_array($direction, ['asc', 'desc'], true) ? $direction : 'desc'; $query->orderBy($sort, $direction);
A06: Diseno Inseguro
Los workflows criticos deben validar invariantes primero: autorizacion, transicion de estado, auditoria.
Archivos:
app/Example/Actions/PublishPostAction.php
use Illuminate\Support\Facades\Gate; Gate::authorize('update', $post);abort_unless((string) $post->getAttribute('status') === 'draft', 422); $post->forceFill(['status' => 'published'])->save();
A07: Fallos de Autenticacion
Usa los patrones de Laravel (regenerar sesion en login, invalidar en logout, abilities en tokens con Sanctum).
Este sitio de documentacion no es un producto de autenticacion, pero aplican los patrones estandar.
Referencia:
resources/docs/es/Vulnerabilidades/FallosAutenticacion.md
A08: Fallos de Integridad de Software o Datos
Nunca confies en ownership enviado por el cliente (ej. user_id en comentarios).
Archivos:
app/Example/Actions/CreateCommentAction.php
Gate::authorize('create', Comment::class); return Comment::query()->create([ 'post_id' => $post->id, 'user_id' => $user->id, 'body' => $body,]);
A09: Fallos de Logging y Alertas de Seguridad
Centraliza contexto por request y emite eventos de seguridad normalizados.
Archivos:
app/Http/Middleware/SecurityLogContext.php
config/logging.php
// app/Http/Middleware/SecurityLogContext.phpLog::withContext([ 'correlation_id' => $correlationId, 'user_id' => $request->user()?->id, 'ip' => $request->ip(), 'user_agent' => $request->userAgent(),]);
// config/logging.php'security' => [ 'driver' => 'stack', 'channels' => explode(',', (string) env('SECURITY_LOG_CHANNELS', 'daily')),],
A10: Mal Manejo de Condiciones Excepcionales
Para clientes JSON en produccion, responde con errores genericos seguros y reporta internamente.
Archivos:
bootstrap/app.php
app/Example/Http/Controllers/CommentModerationController.php
// bootstrap/app.php$exceptions->render(function (Throwable $throwable, Request $request) { if (! $request->expectsJson() || ! app()->isProduction()) return null; if ($throwable instanceof ValidationException) return null; if ($throwable instanceof HttpExceptionInterface) return null; report($throwable); return response()->json(['message' => 'Server error.'], 500);});
// app/Example/Http/Controllers/CommentModerationController.phptry { $isSpam = $spamDetectionService->isSpam($request->string('text')->toString());} catch (SpamServiceTimeout $e) { report($e); return response()->json(['message' => 'Falla temporal.'], 503);}