En resumen: en Nginx con PHP-FPM, la vulnerabilidad de PHP2Shell está en PHP-FPM, no en Nginx. Parchee todas las versiones de PHP-FPM, reinicie por completo cada servicio FPM y demuéstrelo con las horas de inicio de los procesos. Para la seguridad de Nginx con PHP-FPM a largo plazo cuentan los pools separados, los valores que no se pueden sobrescribir y una configuración de Nginx que no ejecute archivos subidos.
Cómo colaboran Nginx y PHP-FPM
Nginx sirve por sí mismo los archivos estáticos y pasa a PHP-FPM, mediante FastCGI, las solicitudes a scripts PHP. PHP-FPM consta de un proceso maestro y varios procesos worker, agrupados en pools. Un worker procesa muchas solicitudes una tras otra.
Eso es justo lo relevante para PHP2Shell. En CVE-2026-109619, el estado interno de una solicitud puede persistir después de liberar la memoria y ser utilizado por una solicitud posterior en el mismo worker. En CVE-2026-109618, una situación de falta de memoria puede interrumpir PHP en plena inicialización de una solicitud. Ambas vulnerabilidades tienen un CVSS v4.0 de 9,2 y están corregidas en PHP 8.5.12, 8.4.27, 8.3.36 y 8.2.35.
Comprobar el inventario: versiones, pools, procesos
Muchos servidores tienen varias versiones de PHP-FPM en paralelo. Los comandos siguientes solo leen; los nombres de paquetes y servicios son ejemplos para Debian/Ubuntu y la familia RHEL.
# ¿Qué servicios FPM están en marcha?
systemctl list-units --type=service | grep -i fpm
# ¿Qué versión hay detrás? (ejemplo Debian/Ubuntu)
php-fpm8.3 -v
# Familia RHEL
php-fpm -v
# ¿A qué sockets o puertos pasa Nginx las solicitudes?
nginx -T 2>/dev/null | grep -E 'fastcgi_pass'
# Pools y sus usuarios
grep -RhE '^\[|^user|^listen' /etc/php/*/fpm/pool.d/ 2>/dev/null
Contraste cada entrada fastcgi_pass con un pool en ejecución. Así ve qué versión de PHP atiende a qué sitio web. En los paquetes de distribución, el parche lo demuestra el registro de cambios con los números CVE, no solo el número de versión.
Un hallazgo frecuente en estos inventarios son servidores en los que, con los años, se instalaron varias versiones de PHP y solo una está realmente en uso. Los demás servicios FPM siguen funcionando igualmente, a menudo con pools estándar que escuchan en un socket o puerto local. Desde fuera suelen no ser accesibles directamente, pero siguen siendo un riesgo innecesario y alargan cualquier actualización de emergencia. Desactive y elimine las versiones que ningún sitio web utilice, una vez comprobada la asignación mediante las entradas fastcgi_pass.
Merece la pena también mirar el usuario de los pools. Si todos los pools funcionan como www-data o nginx, la separación entre los sitios web solo existe sobre el papel. Quien se haga con un sitio web podrá leer entonces los archivos de todos los demás, por ejemplo archivos de configuración con contraseñas de bases de datos. Usuarios de sistema propios por pool y permisos de archivos adecuados crean aquí un límite real. Eso no es un remedio para PHP2Shell, pero limita de forma notable el posible daño de un ataque exitoso.
Instalar el parche y renovar todos los workers
Tras la actualización de los paquetes, reinicie cada servicio PHP-FPM. Una recarga sustituye los workers, pero un reinicio completo renueva también el proceso maestro y es el camino seguro después de las actualizaciones de seguridad.
# Ejemplo Debian/Ubuntu, para cada versión instalada
systemctl restart php8.3-fpm
# Familia RHEL
systemctl restart php-fpm
# Control: hora de inicio del maestro y de los workers
ps -eo pid,lstart,cmd | grep -E 'php-fpm' | grep -v grep
Todos los procesos deben haberse iniciado después de la actualización. Se describe con detalle en Reiniciar PHP-FPM y todos los procesos de trabajo.
Un parche no elimina el software malicioso introducido antes. Conserve los registros de acceso y de errores de Nginx desde el 20/10/2026 y busque rastros en los sitios web.
Fijar valores provisionales en el pool
Mientras una versión no esté parcheada, php.net recomienda medidas provisionales. No ofrecen una protección completa. En PHP-FPM se fijan los valores con php_admin_value o php_admin_flag en el pool; así, las aplicaciones no pueden sobrescribirlos con ini_set().
; Ejemplo: /etc/php/8.3/fpm/pool.d/website.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 24M
Sin función de carga de archivos, php.net indica post_max_size = 8M. En PHP 8.5 se añade fatal_error_backtraces=0. Para la indicación sobre memory_limit=-1 necesita un límite de memoria a nivel de cgroup, por ejemplo mediante MemoryMax en un complemento de systemd para el servicio FPM. Adapte en Nginx client_max_body_size al nuevo post_max_size, para que los usuarios reciban mensajes de error comprensibles. Después, reinicie FPM.
Mejorar a largo plazo la seguridad de Nginx con PHP-FPM
Más allá del caso agudo, ayudan algunas reglas básicas que limitan el daño ante futuras vulnerabilidades:
- Un pool por sitio web, con su propio usuario de sistema, su propio socket y su propio límite
open_basedir. Más en open_basedir y aislamiento por sitio web. - Permisos del socket de modo que solo Nginx pueda acceder; no abra puertos FPM hacia el exterior.
- Ejecutar solo scripts existentes: en la location de PHP, fije
try_files $uri =404;para que Nginx no pase a PHP rutas que no existen. - No ejecutar cargas: prohíba la ejecución de PHP en los directorios de cargas. Encontrará indicaciones en Implementar cargas de archivos de forma segura.
- Proteger las páginas de estado: haga accesibles
pm.status_pathyping.pathsolo internamente.
# Ejemplo: sin ejecución de PHP en el directorio de cargas
location ~* ^/uploads/.*\.php$ {
deny all;
}
Compruebe cada cambio con nginx -t antes de recargar Nginx. Deje constancia además de qué pools, versiones y reglas especiales existen en el servidor. Esta breve documentación ahorra un tiempo valioso ante la próxima vulnerabilidad crítica, porque nadie tiene que buscar primero qué sitio web depende de qué pool.
¿Cómo ayuda PHP SOS en concreto?
- Asignamos cada sitio web a su pool y a su versión de PHP y comprobamos el estado real de los paquetes.
- Parcheamos, reiniciamos todos los servicios FPM y documentamos las horas de inicio de los procesos como prueba.
- Hasta que haya parche, fijamos los valores provisionales por pool y limitamos la memoria mediante cgroup.
- Dentro del refuerzo de servidores separamos correctamente pools, permisos y directorios de cargas.
- Ante la sospecha de una intrusión asumimos la respuesta a incidentes.
Preguntas frecuentes
¿Basta con systemctl reload para PHP-FPM después de una actualización?
Una recarga renueva los workers, pero el proceso maestro puede conservar el código antiguo. Después de una actualización de seguridad, lo seguro es un reinicio completo del servicio. A continuación compruebe la hora de inicio del maestro y de los workers.
¿Hay que reiniciar Nginx después de una actualización de PHP?
Nginx en sí no contiene PHP y no necesita reiniciarse para el parche de PHP. Lo que hay que reiniciar son todos los servicios PHP-FPM. Si además cambia la configuración de Nginx, compruébela con nginx -t antes de recargarla.
¿Por qué pools propios por sitio web?
Cada pool funciona con su propio usuario y sus propios límites. Si un sitio web se ve comprometido, el atacante no accede sin más a los archivos de los demás. Además, los valores provisionales pueden fijarse de forma selectiva por pool.
¿Es pm.max_requests una protección contra PHP2Shell?
No, no sustituye al parche. El valor limita cuántas solicitudes procesa un worker antes de ser reemplazado. Eso puede acortar la vida de los estados en el proceso, pero no es una medida paliativa mencionada por php.net.