Situación actual: PHP2Shell. Dos vulnerabilidades críticas de PHP (CVSS 9.2) permiten ejecutar código malicioso sin autenticación. Parches desde el 21/10/2026.Qué hacer ahora →

Aislar los pools de PHP-FPM por sitio web

Un pool propio por sitio web es el comienzo, no el final. Cómo revisar los pools existentes y reforzar sockets, extensiones, páginas de estado, recursos y logs.

Actualizado 6 min de lecturaRefuerzo y configuración

En resumen: cada sitio web de un servidor debería tener su propio pool de PHP-FPM con usuario de sistema propio y socket propio. La separación solo es eficaz con los detalles: permisos del socket solo para el servidor web, security.limit_extensions en .php, sin páginas de estado accesibles públicamente, límites propios de procesos y de tiempo de ejecución, y logs separados. Frente a PHP2Shell en sí solo protege la actualización; los pools limitan hasta dónde llega un ataque.

Un servidor, muchos sitios web, un riesgo compartido

En muchos servidores ha crecido con los años algo que nadie planeó así. El sitio web de la empresa, la tienda, una página de campaña de 2019 y un sistema de pruebas funcionan uno junto a otro, a menudo con el mismo usuario y en el mismo pool. Si se compromete la página de campaña olvidada, también quedan expuestos los datos de clientes de la tienda.

PHP-FPM, el gestor de procesos FastCGI de PHP, lo resuelve con pools: grupos de procesos worker con ajustes propios. Cómo funciona el principio básico lo explica PHP-FPM explicado: gestor de procesos, pools y workers. La construcción de pools separados con usuario propio, carpeta temporal propia y open_basedir la describe la guía open_basedir y aislamiento por sitio web. Este artículo va un paso más allá: ¿cómo se revisan los pools existentes y qué ajustes hacen fiable la separación?

Una precisión importante: en PHP2Shell, las vulnerabilidades CVE-2026-109618 y CVE-2026-109619 están en el intérprete de PHP, que comparten todos los pools de una versión de PHP. Un pool propio no impide la explotación. Pero decide si, después, un atacante alcanza un sitio web o todos.

Pools de PHP-FPM con usuario propio: revisar lo existente

Antes de cambiar nada, hágase una idea general. Los comandos siguientes solo leen. Las rutas y los números de versión varían según la distribución:

# Todos los pools con usuario y socket
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf

# Pools en ejecución y sus usuarios
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'

# Sockets y sus permisos
ls -l /run/php/

# Comprobar errores en la configuración (no cambia nada)
sudo php-fpm8.3 -t

Fíjese en tres patrones: varios sitios web en el mismo pool, pools que se ejecutan como www-data o incluso como root, y sockets con permiso de escritura para todos los usuarios. Cada uno de ellos socava la separación.

Si en el servidor se ejecutan varias versiones de PHP, cada una tiene su propio servicio FPM con sus propios directorios de pools. Revise todas, no solo la versión que configuró en último lugar. Precisamente las versiones antiguas con pools olvidados son un riesgo, porque a menudo ni se mantienen ni se reinician tras las actualizaciones. Encontrará indicaciones en Varias versiones de PHP en un servidor. Anote el resultado en una pequeña tabla: sitio web, pool, usuario, socket, versión de PHP. Este resumen es además la base de cualquier cambio posterior.

Los ajustes más importantes por pool

El resumen siguiente se apoya en la configuración de FPM del manual de PHP. Los valores de procesos y tiempos dependen de su carga y de su aplicación:

Ajuste Finalidad Recomendación
user, group usuario de los workers usuario propio por sitio web, nunca root
listen.owner, listen.group, listen.mode permisos del socket solo el grupo del servidor web, modo 0660
listen.acl_users acceso por ACL en lugar de propietario registrar solo el usuario del servidor web
security.limit_extensions extensiones ejecutables limitar a .php (por defecto .php .phar)
clear_env vaciar variables de entorno dejar el valor por defecto yes
pm.max_children número máximo de workers limitar de modo que un sitio web no sature el servidor
request_terminate_timeout cancelación de peticiones bloqueadas ajustar a la petición legítima más larga
pm.status_path, ping.path páginas de estado y de ping fijar solo si se necesitan y protegerlas internamente
slowlog, request_slowlog_timeout registro de peticiones lentas en un archivo propio por pool

Un extracto de un pool reforzado tiene este aspecto:

; /etc/php/8.3/fpm/pool.d/shop.conf (extracto)
[shop]
user = shop
group = shop
listen = /run/php/shop.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
request_terminate_timeout = 60s

security.limit_extensions = .php
clear_env = yes

slowlog = /var/log/php/shop.slow.log
request_slowlog_timeout = 10s
php_admin_value[error_log] = /var/log/php/shop.error.log

Ajustes como memory_limit o session.upload_progress.enabled se fijan por pool con php_admin_value y php_admin_flag. Según el manual, estos valores no pueden sobrescribirse con ini_set(). Más información en php_admin_value: bloquear ajustes frente a la sobrescritura.

Valorar bien las páginas de estado y el reciclaje de workers

pm.status_path y ping.path solo están activos si se han fijado. La página de estado muestra peticiones en curso, procesos y carga. Para la monitorización es útil, pero accesible públicamente es una fuente de información innecesaria para los atacantes. Redirija estas rutas en el servidor web solo para direcciones internas. Cómo cooperan de forma segura Nginx y PHP-FPM lo describe Proteger Nginx con PHP-FPM.

pm.max_requests define tras cuántas peticiones se reinicia un worker; por defecto es 0, es decir, nunca. Un valor mayor que cero ayuda contra un consumo de memoria que crece lentamente. No está pensado como protección frente a PHP2Shell: php.net no lo menciona entre las medidas provisionales y subraya que solo la actualización protege por completo. Tras la actualización, además, vale esto: deben reiniciarse todos los workers de todos los pools, porque de lo contrario los procesos antiguos siguen funcionando con el código vulnerable.

Consejo

Mantenga una plantilla para pools nuevos y cree con ella cada sitio web nuevo. Así la separación surge de forma automática, en lugar de añadirla años después.

Errores típicos en el cambio

El error más frecuente es un pool nuevo que se ejecuta con usuario propio, pero cuyos archivos siguen perteneciendo a www-data. Entonces el pool no puede escribir donde debe, y alguien concede los permisos con demasiada generosidad. Aclare antes qué carpetas deben ser escribibles. El segundo error son las tareas programadas que siguen ejecutándose con el usuario antiguo y crean archivos con propietario equivocado. El tercero es una carpeta de sesiones común para todos los pools, que vuelve a anular la separación.

Después de cada cambio, pruebe el inicio de sesión, las subidas, las cachés y las tareas programadas del sitio web, y observe el log de errores del pool.

¿Cómo ayuda PHP SOS en concreto?

  • Registramos todos los pools, usuarios, sockets y ajustes y mostramos dónde los sitios web comparten hoy procesos o permisos.
  • Configuramos por sitio web un pool reforzado, con permisos de socket, límite de extensiones, límites de recursos y logs propios.
  • Protegemos las páginas de estado, reiniciamos todos los workers de forma controlada y probamos cada sitio web.
  • En el marco del refuerzo de servidores documentamos la separación como plantilla para futuros proyectos.

Preguntas frecuentes

¿Por qué debería tener cada sitio web su propio pool de PHP-FPM?

Un pool propio con usuario de sistema propio separa los sitios web a nivel de procesos y de permisos de archivos. Si un sitio web es comprometido, su código no puede leer sin más los archivos de los demás. Además, los límites y ajustes pueden fijarse por separado para cada sitio.

¿Protege un pool propio frente a PHP2Shell?

No. Las vulnerabilidades están en el propio intérprete de PHP, que comparten todos los pools. Solo protege la actualización a una versión corregida con reinicio de todos los workers. Pero los pools separados limitan hasta dónde llega un ataque con éxito.

¿Qué efecto tiene security.limit_extensions?

Según el manual de PHP, el ajuste limita qué extensiones de archivo ejecuta PHP-FPM como script principal. Por defecto son .php y .phar. Limitarlo a .php evita que, por ejemplo, imágenes subidas se ejecuten como PHP si el servidor web está mal configurado.

¿Es peligrosa la página de estado de PHP-FPM?

Revela detalles sobre los procesos y peticiones en curso. Si es accesible públicamente sin protección, facilita la orientación a los atacantes. Según el manual, solo está activa si se ha fijado pm.status_path. Entonces el servidor web debería limitar la ruta a direcciones internas.

Fuentes

Contacto inmediato

Cada hora cuenta. Escríbanos ahora.

Describa brevemente el sistema, la versión de PHP y lo que ha observado. Nos pondremos en contacto, aclararemos la situación y trabajaremos en remoto hasta que el incidente quede cerrado y documentado.

Ayuda inmediata