Volver al Blog
SoftwareSeguridadDesarrollo

Auditoría de Seguridad y Protección OWASP en Web Apps con Next.js 15, Redis Rate Limiting y CSP Estricto

Brayan Developer
4 min de lectura
Auditoría de Seguridad y Protección OWASP en Web Apps con Next.js 15, Redis Rate Limiting y CSP Estricto
Guía práctica para blindar aplicaciones web Next.js contra vulnerabilidades OWASP Top 10 mediante Content Security Policy (CSP), Rate Limiting con Upstash Redis y Server Action guards.

En el desarrollo de plataformas SaaS y aplicaciones empresariales modernas, la velocidad de desarrollo jamás debe comprometer la seguridad de la infraestructura y los datos de los usuarios. Con el auge de Next.js 15 Server Components y Server Actions, surgen nuevos vectores de ataque que van más allá del clásico Cross-Site Scripting (XSS) y SQL Injection. En este artículo detallamos las medidas técnicas obligatorias para auditar y blindar aplicaciones web cumpliendo los estándares de OWASP Top 10.

Portada

Principales Vectores de Vulnerabilidad en Next.js Moderno#

El framework Next.js ofrece defensas nativas sólidas, pero requiere configuración explícita en los siguientes frentes críticos:

  1. Ataques de Fuerza Bruta y DoS a Server Actions: Sin limitación de peticiones por IP/Usuario, los endpoints serverless pueden ser sobrecargados.
  2. Inyección de Scripts (XSS) y Clickjacking: Mitigados mediante encabezados HTTP de seguridad y un Content Security Policy (CSP) estricto con nonces criptográficos.
  3. Broken Object Level Authorization (BOLA / IDOR): Acceso no autorizado a registros modificando IDs en parámetros de mutaciones.

1. Rate Limiting Distribuido con Upstash Redis en Middleware#

Para prevenir ataques de fuerza bruta en rutas de login, recuperación de contraseñas y envío de formularios, implementamos un limitador de tasa basado en el algoritmo Sliding Window Counter:

// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/request';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

// Permitir 10 peticiones por ventana de 60 segundos por IP
const ratelimit = new Ratelimit({
  redis: redis,
  limiter: Ratelimit.slidingWindow(10, '60 s'),
  analytics: true,
  prefix: '@ratelimit/api',
});

export async function middleware(request: NextRequest) {
  // Aplicar Rate Limiting a rutas de autenticación y mutaciones críticas
  if (request.nextUrl.pathname.startsWith('/api/auth') || request.nextUrl.pathname.startsWith('/api/checkout')) {
    const ip = request.ip ?? request.headers.get('x-forwarded-for') ?? '127.0.0.1';
    const { success, limit, remaining, reset } = await ratelimit.limit(ip);

    if (!success) {
      return new NextResponse(
        JSON.stringify({
          error: 'Demasiadas solicitudes. Por favor intente más tarde.',
          retryAfter: reset,
        }),
        {
          status: 429,
          headers: {
            'Content-Type': 'application/json',
            'X-RateLimit-Limit': limit.toString(),
            'X-RateLimit-Remaining': remaining.toString(),
            'X-RateLimit-Reset': reset.toString(),
          },
        }
      );
    }
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/api/:path*'],
};

2. Content Security Policy (CSP) Estricto con Nonce Criptográfico#

Un CSP estricto prohíbe la ejecución de scripts inline maliciosos y restringe los orígenes de carga de recursos a dominios de confianza:

// src/lib/securityHeaders.ts
import { headers } from 'next/headers';

export function getSecurityHeaders() {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');

  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https: 'unsafe-inline';
    style-src 'self' 'unsafe-inline';
    img-src 'self' blob: data: https://brayan.es;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
  `.replace(/\s{2,}/g, ' ').trim();

  return {
    headers: {
      'Content-Security-Policy': cspHeader,
      'X-Content-Type-Options': 'nosniff',
      'X-Frame-Options': 'DENY',
      'X-XSS-Protection': '1; mode=block',
      'Referrer-Policy': 'strict-origin-when-cross-origin',
      'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
    },
    nonce,
  };
}

3. Blindaje de Server Actions contra BOLA / IDOR#

Toda Server Action debe validar estrictamente el esquema de entrada con Zod y comprobar la pertenencia del recurso (tenant-isolation):

// src/actions/invoiceActions.ts
'use server';

import { z } from 'zod';
import { prisma } from '@/lib/prisma';
import { getAuthenticatedUser } from '@/lib/auth';

const DeleteInvoiceSchema = z.object({
  invoiceId: z.string().cuid(),
});

export async function deleteInvoiceAction(formData: FormData) {
  // 1. Autenticación del usuario de la sesión
  const user = await getAuthenticatedUser();
  if (!user) {
    throw new Error('No autorizado');
  }

  // 2. Validación de entrada tipada
  const parsed = DeleteInvoiceSchema.safeParse({
    invoiceId: formData.get('invoiceId'),
  });

  if (!parsed.success) {
    return { success: false, error: 'Datos inválidos' };
  }

  // 3. Verificación de propiedad (Evita IDOR)
  const invoice = await prisma.invoice.findFirst({
    where: {
      id: parsed.data.invoiceId,
      organizationId: user.organizationId, // Aislamiento por inquilino
    },
  });

  if (!invoice) {
    return { success: false, error: 'Comprobante no encontrado o sin permisos' };
  }

  await prisma.invoice.delete({
    where: { id: parsed.data.invoiceId },
  });

  return { success: true };
}

Checklist de Auditoría de Ciberseguridad Web#

  • Rate Limiting: Implementado en endpoints de login, recuperación de clave y pagos.
  • CSP y Encabezados: X-Frame-Options: DENY, nosniff y CSP estricto activados.
  • Sanitización de Entradas: Validación rigurosa con esquemas Zod en todas las Server Actions y APIs.
  • Aislamiento Multi-Tenant: Filtros RLS y validación de organizationId en todas las consultas de base de datos.
  • Gestión Segura de Secretos: Variables de entorno protegidas sin exposición al cliente (NEXT_PUBLIC_).

Conclusión#

Garantizar la seguridad de una aplicación web moderna exige una estrategia de defensa en profundidad, combinando Rate Limiting distribuido, aislamiento estricto de datos y encabezados HTTP robustos.

¿Necesitas auditar la seguridad de tu plataforma web o SaaS antes de salir a producción? Descubre nuestros servicios de Consultoría y Desarrollo de Software o contáctanos para agendar una auditoría técnica.

Etiquetas
Seguridad WebOWASPNext.jsRedisRate LimitingCSPCiberseguridad
Compartir:XLinkedInWhatsApp

¿Te gustaría profundizar en estos temas?

Aprende sobre desarrollo de software, apps a medida, automatizaciones con N8N, Next.js y Cloud con casos reales.

Hablemos