← Guías por stack

Next.js

Seguridad para tu app Next.js

Next.js hace fácil mezclar código de servidor y de cliente, y ahí es donde se cuelan la mayoría de fallos: una variable NEXT_PUBLIC de más, una API route sin proteger o unos source maps en producción. Escanea tu app y sal de dudas en 30 segundos.

Riesgos típicos en Next.js

Secretos en el bundle por NEXT_PUBLIC

Todo lo que lleva el prefijo NEXT_PUBLIC_ viaja al navegador. Una clave privada ahí queda expuesta para siempre a cualquiera que abra las DevTools.

API routes sin autorización

Una API route es una URL pública. Si solo escondes el botón en la interfaz pero no compruebas la sesión en el handler, cualquiera puede llamarla directamente.

Source maps y cabeceras por defecto

Next.js no añade cabeceras de seguridad por ti, y muchos builds publican source maps que dejan reconstruir tu código. Ambas cosas se arreglan en next.config.

Casi todos los agujeros de una app Next.js hecha con IA vienen de una sola confusión: no saber qué parte del código corre en el servidor y qué parte acaba en el navegador. Next.js mezcla las dos en el mismo proyecto, y Cursor, v0 o Bolt cruzan la frontera sin avisar. Esta guía explica ese modelo mental en cinco minutos, repasa lo que las herramientas de IA hacen mal en Next.js, te deja un bloque de reglas para pegar en tu proyecto, un prompt de revisión y cuatro comprobaciones desde fuera. La lista punto por punto de qué arreglar está en la checklist de Next.js antes de lanzar; aquí va el porqué.

Parte 1: dónde corre cada cosa

Cinco lugares, dos mundos

En el App Router hay cinco sitios donde puede vivir tu código: Server Components (solo servidor, nunca llegan al navegador), Client Components (marcados con "use client", se envían al navegador enteros), Route Handlers (app/api/.../route.ts, URLs públicas), Server Actions (funciones con "use server" que el cliente invoca por POST) y el middleware (llamado proxy en las versiones más recientes), que corre antes de cada petición. Los tres últimos y los Server Components son el mundo del servidor; los Client Components son el mundo del navegador. Todo lo que importas en un Client Component, incluidas constantes y funciones de ayuda, viaja al bundle.

Lo que llega al navegador

  • Variables de entorno con prefijo NEXT_PUBLIC_, incrustadas en el momento del build. Las demás solo existen en el servidor.
  • Cualquier dato que un Server Component pase como prop a un Client Component. Si pasas el objeto user completo, viaja con el hash de la contraseña y el email.
  • Los archivos de la carpeta public/, tal cual.
  • Los source maps, si los activas con productionBrowserSourceMaps.

Tres mecanismos que evitan cruzar la frontera

El paquete server-only: un import 'server-only' al principio de un archivo hace que el build falle si alguien lo importa desde un Client Component. Úsalo en todo lo que toque la base de datos o lea secretos. Una capa de acceso a datos: un módulo que centraliza la comprobación de sesión y devuelve solo los campos necesarios (un DTO), en lugar de consultar la base de datos desde cada página. Y la regla de que cada punto de entrada comprueba la sesión: Route Handlers, Server Actions y Server Components que lean datos privados, cada uno por su cuenta. El middleware no sustituye esa comprobación, como demostró la CVE-2025-29927.

Lo que Next.js no hace por ti

No añade cabeceras de seguridad, no limita peticiones, no valida la entrada de las Server Actions y no protege rutas por el hecho de que estén en una carpeta llamada (admin). Todo eso es código tuyo o configuración en next.config.

Parte 2: lo que la IA hace mal en Next.js

  • Todo con "use client". Cuando un componente necesita estado, la IA lo marca de cliente y arrastra con él la consulta a la base de datos o la clave de la API. Divide: el componente de servidor obtiene los datos, el de cliente solo pinta.
  • Protección solo en el middleware. El middleware redirige si no hay cookie y el handler confía en ello. Comprueba la sesión también en el handler; es lo que la CVE del middleware enseñó, y versiones anteriores a 15.2.3 o 14.2.25 siguen siendo vulnerables.
  • Route Handlers y Server Actions sin autenticación ni validación. Una Server Action es un endpoint público aunque no tenga URL visible. Comprueba quién llama y valida el cuerpo con un esquema (zod o similar) antes de tocar nada.
  • Secretos con prefijo público. NEXT_PUBLIC_OPENAI_KEY porque el componente de cliente "lo necesitaba". La clave va a un Route Handler y el cliente llama al handler.
  • remotePatterns con comodín. hostname: '**' en la configuración de imágenes convierte tu optimizador de imágenes en un proxy abierto para cualquier URL.
  • Redirecciones con parámetro. redirect(searchParams.next) sin comprobar que el destino es una ruta interna: phishing con tu dominio.
  • fetch a URLs del usuario. Un handler que descarga lo que le pasen puede llegar a servicios internos de tu red o del proveedor.
  • dangerouslySetInnerHTML con contenido de usuario. XSS clásico; si necesitas HTML, sanitízalo en el servidor.
  • Sin cabeceras ni límites. Ni CSP, ni HSTS, ni X-Frame-Options, y un endpoint que llama a un modelo de pago sin tope por usuario.

Un bloque de reglas para tu proyecto

Pégalo en el archivo de reglas que lea tu herramienta (.cursor/rules, AGENTS.md o el equivalente) para que se aplique en cada generación:

Reglas de seguridad para este proyecto Next.js:
- Los componentes son Server Components salvo que necesiten estado o eventos del navegador. Un Client Component nunca importa módulos que accedan a la base de datos o lean process.env sin prefijo.
- Todo módulo que toque la base de datos o secretos empieza con import 'server-only'.
- Nunca uses NEXT_PUBLIC_ para una clave secreta. Las llamadas a servicios con clave van en un Route Handler o una Server Action.
- Cada Route Handler y cada Server Action comprueba la sesión y valida la entrada con un esquema antes de hacer nada. El middleware no cuenta como autenticación.
- Pasa a los Client Components solo los campos necesarios, nunca el objeto completo del usuario o de la fila.
- Las redirecciones solo aceptan rutas internas que empiecen por una sola barra.
- images.remotePatterns lista dominios concretos, nunca un comodín de host.
- No uses dangerouslySetInnerHTML con contenido de usuario.
- next.config define las cabeceras de seguridad y no activa productionBrowserSourceMaps.

Un prompt de revisión antes de desplegar

Revisa la seguridad de esta app Next.js y no cambies nada todavía. Responde con una lista:
1. Client Components que importen módulos de base de datos, lean secretos o reciban como prop objetos con campos sensibles.
2. Route Handlers y Server Actions sin comprobación de sesión o sin validación de entrada.
3. Rutas cuya única protección sea el middleware.
4. Variables NEXT_PUBLIC_ que contengan claves secretas.
5. Redirecciones que acepten un destino del usuario, fetch a URLs del usuario y usos de dangerouslySetInnerHTML.
6. remotePatterns con comodín, productionBrowserSourceMaps activado y cabeceras de seguridad ausentes en next.config.
7. Versión de Next.js y si es anterior a 15.2.3 o 14.2.25.
Para cada punto, propón el cambio exacto y espera mi confirmación.

Comprueba el resultado desde fuera

Cuatro comandos contra tu dominio. Cabeceras y versión del middleware:

curl -sI https://tudominio.com | grep -iE "strict-transport|content-security|x-frame|x-content-type"
curl -s -o /dev/null -w "%{http_code}\n" -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" https://tudominio.com/dashboard

Si el segundo devuelve 200 en una ruta que debería redirigir al login, tu versión es vulnerable a la CVE del middleware: actualiza hoy. Después, source maps y rutas de API que el bundle revela:

curl -s https://tudominio.com | grep -oE '/_next/static/chunks/[^"]+\.js' | head -3
curl -s https://tudominio.com/_next/static/chunks/main-app-XXXX.js | grep -oE '"/api/[^"]+"' | sort -u

Pide cada ruta de API sin sesión: lo correcto es un 401 o 403. Scanaris hace estas comprobaciones y unas cuarenta más en 30 segundos: detecta que la app es Next.js y su versión, prueba la cabecera de la CVE, busca secretos en los chunks, comprueba si los source maps están publicados, revisa cabeceras, CSP, cookies, CORS y archivos expuestos, y te da un fix prompt por cada fallo para pegar en Cursor o v0. Si despliegas en Vercel, la guía de Vercel cubre las previews públicas y las variables de entorno.

Preguntas frecuentes

¿Las Server Actions son seguras por defecto?

Son más difíciles de descubrir que un Route Handler, pero igual de públicas: cualquiera que tenga el identificador de la acción puede invocarla. Trátalas como un endpoint: sesión y validación dentro de la función.

¿Basta con el middleware para proteger el panel?

No. Sirve para redirigir rápido, pero la comprobación real va en cada página, handler y acción. La CVE-2025-29927 permitía saltarse el middleware con una cabecera; aunque estés en una versión parcheada, la regla sigue siendo la misma.

¿Puedo seguir usando Pages Router?

Sí, y el modelo es el mismo: getServerSideProps y pages/api son el servidor, el resto es cliente. Devuelve de getServerSideProps solo lo que la página necesita, porque todo lo que devuelves se serializa en el HTML.

Checks relacionados

¿Tu app Next.js pasa estos checks?

Escanéala gratis y recibe un prompt de arreglo por cada fallo que encontremos.

Escanear mi web gratis