← Blog
·8 min read

CVE-2025-55183: Cuando tu Server Function de Next.js devuelve su propio código fuente

Una petición maliciosa hacía que las Server Functions de Next.js devolvieran su código fuente en la respuesta HTTP. Si tenías una API key hardcodeada — aunque fuera 'solo para probar' — era legible.

Enrique Alvarez

Security Engineer

En el mundo de los CVEs de seguridad, los números CVSS mandan la atención. Un CVSS 10 como React2Shell (CVE-2025-55182) entra con sirenas y todo el mundo actualiza. Un CVSS 5.3 suena a "severidad media" y mucha gente lo ignora.

CVE-2025-55183 fue eso: el CVE callado del cluster de React2Shell. Ningún RCE, ningún shell remoto, ningún headline que gritara "actualiza ahora". Solo una petición HTTP diseñada que hacía que tu Server Function devolviera su propio código fuente en la respuesta.

El problema no fue técnico — fue de hábito. Cursor escribió tu Server Action con una API key de placeholder. Copilot sugirió el patrón const API_KEY = 'sk-proj-...' y pusiste la tuya. Claude te dio el boilerplate completo "solo reemplaza la key". Y muchos developers, con deadline encima y app funcionando, deployaron sin hacer ese reemplazo. Con CVE-2025-55183, cualquier atacante con la petición correcta podía leer ese literal de cadena directamente en la respuesta HTTP.


Qué es CVE-2025-55183 (y qué no es)

CVE-2025-55183 es una vulnerabilidad de divulgación de información en Next.js App Router. CVSS 5.3 — severidad media.

No es ejecución remota de código. No permite tomar el servidor. No lee tu base de datos ni tus variables de entorno.

Lo que sí hace: en condiciones específicas, una petición HTTP maliciosa puede provocar que una Server Function devuelva fragmentos de su propio código fuente dentro del cuerpo de la respuesta.

El mecanismo está en el protocolo React Flight — el formato de serialización que Next.js usa para comunicar Server Components y Server Actions con el cliente. Cuando el servidor procesa una petición de Server Function, serializa la respuesta usando este protocolo. La falla permitía manipular esa serialización para que incluyera el texto del código fuente de la función en lugar de — o junto con — la respuesta esperada.

Lo que queda expuesto son literales de cadena en el código fuente. String constants. Variables declaradas con un valor hardcodeado. No referencias de runtime como process.env.KEY — esas siguen siendo seguras. Solo los valores escritos directamente en el archivo.

Los secretos cargados desde variables de entorno con process.env.API_KEY no estuvieron expuestos. El problema es específicamente con string literals: const API_KEY = 'sk-prod-abc123'.

Versiones afectadas: Las mismas que el cluster React2Shell — Next.js 14.x por debajo de 14.2.35 y Next.js 15.x por debajo de 15.2.4.

Fix: Actualizar a Next.js 14.2.35+ o 15.2.4+.


Qué ve un atacante exactamente

Imagina esta Server Function en tu proyecto:

// app/actions/reports.js
'use server'
 
// MAL: literal de cadena — queda expuesto con CVE-2025-55183
const OPENAI_API_KEY = 'sk-proj-abc123xyz987...'
const INTERNAL_WEBHOOK = 'https://hooks.internal.company.com/alerts?token=secret-token-prod'
 
export async function generarResumen(texto) {
  const res = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${OPENAI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ model: 'gpt-4o', messages: [{ role: 'user', content: texto }] }),
  })
  return res.json()
}

Un atacante que aprovecha CVE-2025-55183 no necesita encontrar un endpoint documentado ni hacer fuzzing de rutas. Manda una petición especialmente diseñada al endpoint de la Server Function y la respuesta puede incluir el código de la función — incluyendo sk-proj-abc123xyz987... y la URL del webhook interno.

Con esa key de OpenAI puede generar tokens a tu costa. Con la URL interna puede investigar tu infraestructura. Y todo sin activar un solo log de autenticación fallida, porque nunca intentó autenticarse — tu propio servidor le mandó los datos.


La línea de código que lo causó

No fue una falla exótica. Fue el patrón más común de "lo arreglo después" que existe en el desarrollo con IA.

Mira la diferencia:

// app/actions/ai.js
'use server'
 
// MAL: string literal hardcodeado — vulnerable a CVE-2025-55183
const OPENAI_API_KEY = 'sk-proj-abc123xyz...'
 
export async function resumirTexto(input) {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    headers: { Authorization: `Bearer ${OPENAI_API_KEY}` },
    // ...
  })
  return response.json()
}
// app/actions/ai.js
'use server'
 
// BIEN: referencia de runtime — el valor nunca existe como literal en el código fuente
export async function resumirTexto(input) {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
    // ...
  })
  return response.json()
}

La distinción técnica es importante: process.env.OPENAI_API_KEY es una referencia que se resuelve en tiempo de ejecución. El valor nunca está escrito en tu código fuente. CVE-2025-55183 solo puede exponer lo que está escrito — los literales.

const KEY = 'sk-proj-abc123' está escrito. Es un literal. Eso es lo que la falla filtra.


Cómo los asistentes de IA generan este patrón por defecto

Esta es la parte que hace a CVE-2025-55183 más relevante de lo que su CVSS sugiere.

Los asistentes de código — Cursor, GitHub Copilot, v0, Claude — generan Server Actions con frecuencia usando el patrón de placeholder hardcodeado. No por negligencia: es para que el código sea ejecutable de inmediato, sin que el developer tenga que configurar nada antes de ver que funciona.

Un prompt como "crea una Server Action que llame a la API de OpenAI para resumir texto" casi siempre genera algo así:

'use server'
 
const OPENAI_API_KEY = 'tu-api-key-aqui' // Reemplaza con tu key real
 
export async function summarize(text: string) {
  // ...
}

El comentario dice "reemplaza con tu key real". Pero en la práctica:

  1. El developer pone la key real para probar que funciona.
  2. La función funciona.
  3. El developer sigue con la siguiente tarea.
  4. El // TODO: mover a .env nunca llega.
  5. Se hace commit. Se deploya.

Estudiamos patrones en repos públicos de GitHub — el patrón const ... = 'sk-proj- dentro de archivos con 'use server' aparece con una frecuencia que incomoda. No son developers descuidados: son developers que van rápido con herramientas que generan código inseguro por default.

Data Hogo detecta exactamente este patrón — string literals que parecen secrets (API keys, tokens, contraseñas) dentro de archivos de Server Actions — como parte de sus 199 checks de seguridad. Si querés ver si tu repo tiene este problema antes de revisarlo manualmente: escanea tu repo gratis en datahogo.com.


Cómo auditar tus Server Functions ahora mismo

Dos pasos. Primero, verifica tu versión de Next.js:

npm list next

Necesitas ver 14.2.35 o superior, o 15.2.4 o superior. Si estás atrás:

npm install next@latest

Segundo, busca string literals que parezcan secrets en tus archivos server. El grep de abajo no es perfecto — tiene falsos positivos — pero te da un punto de partida en menos de 30 segundos:

# Encuentra archivos con 'use server' que también contengan patrones de secrets hardcodeados
grep -r "use server" . --include="*.ts" --include="*.tsx" --include="*.js" -l \
  | xargs grep -l "sk-\|api_key\|API_KEY\|secret\|token\|password\|passwd"

Los archivos que aparezcan en esa lista merecen revisión manual. Busca específicamente declaraciones de variable con string literals — no referencias a process.env.

Si encuentras algo así:

const STRIPE_SECRET = 'sk_live_abc123...'
const DB_PASSWORD = 'hunter2'
const WEBHOOK_TOKEN = 'whsec_xyzxyzxyz'

Trátalos como comprometidos. Rota los credentials, revoca los anteriores, y mueve los nuevos a variables de entorno.


Tres cosas que hacer hoy

1. Actualiza Next.js si no lo has hecho.

npm install next@latest

Verifica después:

npm list next
# next@15.2.4 o superior — bien
# next@15.1.x o inferior — actualiza

2. Audita tus Server Actions en busca de literals hardcodeados.

El grep de arriba es el punto de partida. Después de identificar archivos sospechosos, la regla es simple: si el valor está escrito directamente en el código, muévelo a .env.local y accédelo con process.env.

3. Si encontraste un secret hardcodeado, rótalo — aunque hayas actualizado.

CVE-2025-55183 pudo haber estado activo en tu app antes de que actualizaras. Si el secret estuvo en el código durante ese tiempo, un atacante pudo haberlo leído. Actualizar Next.js cierra la falla, pero no deshace lo que ya fue expuesto.

La rotación es el paso que mucha gente omite porque "nadie nos atacó". Quizás no. Pero tampoco tendrías un log de eso — la explotación era pasiva, sin errores de autenticación, sin tráfico anormal evidente.


TL;DR

  • CVE-2025-55183 es la hermana callada de React2Shell. CVSS 5.3, no CVSS 10. Sin RCE, pero con filtración de código fuente.
  • Una petición HTTP maliciosa podía hacer que una Server Function devolviera string literals de su propio código — incluyendo API keys hardcodeadas.
  • process.env.API_KEY nunca estuvo en riesgo. Solo los valores escritos directamente en el código: const KEY = 'sk-proj-...'.
  • Los asistentes de IA (Cursor, Copilot, v0) generan este patrón inseguro por default. El developer que va rápido a menudo no hace el reemplazo antes de deployar.
  • Fix: actualizar a Next.js 14.2.35+ o 15.2.4+.
  • Si encontraste un literal hardcodeado: muévelo a .env, rota el credential, y asume que pudo haber sido leído.
  • Los secrets en process.env están seguros. Los string literals directos en el código fuente no lo están.
CVENext.jsServer Actionssecretsfiltración de códigoApp Routervulnerabilidades