Volver al blog
SeguridadPHPDevOps

Fallo crítico en PHP: RCE sin login, actualiza ya

22 de octubre de 2026·7 min de lectura·Diego Horvatti

Ayer, 21 de octubre, el equipo de PHP publicó un aviso con el título más directo posible: vulnerabilidades críticas de ejecución remota de código, actualiza ya. Es un fallo crítico en PHP que no necesita login y, en algunos escenarios, se dispara antes de que corra una sola línea de tu aplicación. Si tienes un servidor PHP respondiendo peticiones en internet, este es tu problema de hoy.

Lo sé. Escribes TypeScript, usas Bun, despliegas en Vercel. Pero haz una cuenta rápida: el blog del cliente en WordPress, el panel viejo en Laravel que "nadie toca", ese phpMyAdmin olvidado en un subdominio. PHP todavía mueve una parte enorme de la web, y es muy probable que al menos uno de esos sea tuyo o de alguien que te va a llamar.

Qué pasó con PHP

Son dos vulnerabilidades, ambas con nota CVSS v4.0 de 9.2 según el preaviso del día 20. Las correcciones salieron en las versiones 8.5.12, 8.4.27, 8.3.36 y 8.2.35. La fuente oficial está en el archivo de noticias de php.net.

La primera, CVE-2026-109618, tiene que ver con la falta de memoria. Cuando PHP supera el límite en medio de una petición, puede detenerse con datos internos a medio construir. Después, en la limpieza de la petición, esos datos rotos se tratan como válidos. Memoria corrupta que el atacante puede provocar es el camino clásico hacia la ejecución de código.

La segunda, CVE-2026-109619, trata de estado que sobrevive más de la cuenta. Un trozo de estado interno de una petición sigue apuntando a memoria ya liberada. La siguiente petición que atiende el mismo worker puede usar esa basura antes de que la inicialización normal lo limpie todo. Un use-after-free clásico, con el mal detalle de cruzar entre peticiones.

Quién está en riesgo de verdad

Respuesta corta: casi todos los que sirven web con PHP. El aviso menciona PHP-FPM, Apache con mod_php, php-cgi, FrankenPHP y LiteSpeed.

El escenario más grave es mod_php. El equipo de PHP dice que existe un exploit demostrado en el que una petición sin autenticación dispara el ataque antes de que se ejecute cualquier código de la aplicación. O sea, da igual si tu Laravel lo valida todo bien. El problema ocurre antes de que Laravel despierte.

No sirve de nada tener el código más seguro del mundo si el runtime cae antes que él.

Un punto que mucha gente va a ignorar: PHP 8.1 y anteriores ya no reciben actualizaciones oficiales. Si tu servidor sigue con 7.4, estás en el grupo que más necesita actuar y el que menos opciones listas tiene.

Cómo saber qué versión usas

Antes que nada, averigua qué hay en producción. No confíes en lo que dice el composer.json, confía en el binario:

php -v
# o, para la versión que usa de verdad el servidor web:
php-fpm -v

El CLI y el FPM pueden estar en versiones distintas en la misma máquina. Lo he visto más veces de las que me gustaría. Si puedes crear una ruta temporal, un phpinfo() detrás de autenticación muestra la versión real que atiende las peticiones. Después borra la ruta.

Si usas Docker, mira la tag de la imagen:

# mal: puede quedarse atascada en una versión vulnerable durante meses
FROM php:8.3.10-fpm

# mejor: sigue los parches de la línea 8.3
FROM php:8.3-fpm

Fijar la tag en un parche da reproducibilidad, pero sale caro en un día como hoy. Si la vas a mantener fija, que exista un proceso para subir la versión.

Cómo actualizar sin drama

El camino correcto es uno solo: subir a 8.5.12, 8.4.27, 8.3.36 u 8.2.35, según tu línea.

  • Distribuciones Linux recibieron los parches antes, así que ejecuta el update del paquete de tu distro y reinicia FPM o Apache.
  • Docker: rebuild de la imagen con --pull y redeploy. Sin --pull, reconstruyes sobre la caché vieja y crees que actualizaste.
  • Windows: binarios nuevos en la página de descargas.
  • Hosting compartido: abre un ticket hoy y pregunta la versión exacta. "Lo estamos revisando" no es una respuesta.
# Debian/Ubuntu con paquetes de la distro
sudo apt update && sudo apt upgrade php8.3-fpm
sudo systemctl restart php8.3-fpm
php-fpm8.3 -v

Hay un detalle que pilla a mucha gente: si usas el repositorio rpms.remirepo.net, mantenido por voluntarios, el propio aviso dice que no tendrá actualización hoy. La recomendación es cambiar a paquetes de la distribución o compilar desde el código fuente. No es una crítica al mantenedor, que hace un trabajo enorme gratis. Es solo la realidad de depender de una persona en un día de incendio.

Para quien está atascado en 7.4, 8.0 u 8.1, el equipo de seguridad publicó backports mínimos en ramas de GitHub, pensados para quien compila desde el código fuente o no tiene un proveedor de LTS. Es un gesto raro para versiones sin soporte, y muestra el tamaño del asunto.

¿Y si no puedo actualizar hoy?

Pasa. Ventana de mantenimiento, un cliente que tiene que aprobar, un servidor del que nadie sabe la contraseña (esto último es broma, pero no tanto). El aviso enumera mitigaciones, con una advertencia honesta: reducen caminos de ataque concretos, no dejan la instalación segura.

Para el fallo de falta de memoria:

; en el pool de FPM, para que la aplicación no lo sobrescriba
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M

La idea es eliminar el desbordamiento de memoria que el atacante puede controlar y reducir el tamaño de lo que puede enviar. Si la aplicación necesita subidas, el aviso sugiere post_max_size de 32M, con upload_max_filesize por debajo de eso. Sin límite de memoria en PHP, quien protege la máquina es el contenedor o el cgroup, a ser posible matando solo el worker afectado. Y no lo cambies por RLIMIT_AS, que justamente puede provocar el fallo.

También conviene ajustar límites en el proxy, como Nginx o el balanceador: tamaño del cuerpo, número de campos, partes de multipart, cantidad de headers. Incluso para peticiones sin un Content-Length fiable.

Para el fallo de estado que sobrevive entre peticiones:

session.upload_progress.enabled = 0
; solo en PHP 8.5
fatal_error_backtraces = 0

Esto tiene que ir en el php.ini, en el pool de FPM o en la configuración del servidor. ini_set() y .user.ini no sirven, porque entran demasiado tarde. Y reinicia todos los workers, si no la configuración nueva no aplica a los procesos que ya están en marcha.

Un detalle que me pareció interesante: ocultar errores con @ o display_errors=0 no ayuda. El camino vulnerable es que el diagnóstico se emita, no que se muestre. Es un buen recordatorio de que ocultar un warning nunca fue lo mismo que corregirlo.

Qué cambia esto en tu flujo

La lección práctica no es sobre PHP. Es sobre el tiempo entre "salió el parche" y "está en producción". En la mayoría de los equipos que conozco, ese tiempo se mide en semanas, porque nadie es dueño de la actualización del runtime. El código de la aplicación tiene PR, review y CI. La imagen base se queda ahí, parada, hasta que alguien se acuerda.

Algunas cosas que valen para cualquier stack, incluida la tuya de Node o Bun:

  • Que puedas listar en cinco minutos todos los runtimes que tienes en producción y sus versiones.
  • Usa una tag de imagen que siga los parches, o automatiza el bump con Renovate o Dependabot.
  • Ten un deploy que puedas lanzar un martes por la tarde sin miedo.

Mi opinión, y aquí sin medias tintas: si todavía usas mod_php en 2026, esta es la excusa que faltaba para migrar a PHP-FPM o FrankenPHP detrás de un proxy. No porque FPM sea inmune, no lo es. Sino porque separar PHP del servidor web te da más sitios donde poner límites cuando aparezca el próximo aviso como este. Y va a aparecer.

Actualiza hoy, aplica las mitigaciones donde no puedas, y apunta cuánto tardaste. Ese número dice más sobre la salud de tu infra que cualquier dashboard. Si quieres ver cómo organizo el deploy y la infraestructura en mis propios proyectos, echa un vistazo a lo que estoy construyendo.

Resumen para LinkedIn

Ayer PHP publicó un aviso sin rodeos: fallo crítico, ejecución remota de código sin login, actualiza ya.

En mod_php el ataque se dispara antes de que corra una sola línea de tu aplicación. Tu Laravel puede validar todo, porque el problema ocurre antes de que despierte.

"Pero yo uso TypeScript y Bun." Vale. ¿Y el WordPress del cliente, el panel viejo que nadie toca, el phpMyAdmin olvidado en un subdominio?

Las versiones corregidas son 8.5.12, 8.4.27, 8.3.36 y 8.2.35. Compruébalo en el binario, no en el composer.json. Y si tu servidor sigue en 8.1 o anterior, no hay actualización oficial para él.

Para mí, la lección más grande ni siquiera es sobre PHP. Es sobre cuánto tarda tu equipo desde que sale el parche hasta que llega a producción.

En el artículo completo explico cómo comprobar la versión, actualizar sin drama y qué se puede mitigar si no puedes subir hoy. ¿Cuánto tardas tú en aplicar un parche así?

#PHP #SeguridadInformática #DevOps #DesarrolloWeb #Infraestructura