Cómo aislar sitios web con pools de PHP-FPM: un sitio, un usuario
Cuando todos los sitios de un servidor comparten un usuario de PHP, un plugin vulnerado puede alcanzarlos a todos. Los pools de PHP-FPM por sitio contienen el daño.
Si un servidor aloja varios sitios web, dar a cada sitio su propio pool de PHP-FPM, ejecutado con su propio usuario de Linux, es una de las formas más eficaces de contener una intrusión. Cuando todos los sitios funcionan con el mismo usuario, un atacante que entra por un plugin de WordPress desactualizado normalmente puede leer y modificar todos los demás sitios del servidor, incluidas sus contraseñas de base de datos. Con un pool y un usuario del sistema por sitio, más los permisos de archivo correctos, el atacante queda en gran medida encerrado en el sitio que vulneró. Esta guía explica cómo funciona, qué configurar y dónde están los límites.
Qué son PHP-FPM y los pools
PHP-FPM (FastCGI Process Manager) es la forma estándar de ejecutar PHP detrás de servidores web como Nginx, Apache y LiteSpeed. En lugar de que PHP funcione dentro del servidor web, FPM mantiene un conjunto de procesos de PHP listos y el servidor web les pasa las solicitudes.
Un pool es un grupo de esos procesos con su propia configuración. El manual de PHP lo resume así: con FPM se pueden ejecutar varios pools de procesos con configuraciones distintas. Los ajustes clave de cada pool incluyen:
userygroup: la cuenta de Linux con la que se ejecutan los procesos (obligatorio en cada pool).listen: el socket o puerto donde el pool recibe las solicitudes del servidor web.pmypm.max_children: cuántos procesos puede ejecutar el pool.php_admin_value/php_admin_flag: ajustes de PHP para ese pool que los scripts no pueden cambiar conini_set().
Por qué "un sitio, un usuario" contiene el daño
Linux decide a qué puede acceder un proceso según el usuario con el que se ejecuta. Por eso el ajuste user del pool es la verdadera barrera de seguridad.
Usuario compartido (habitual en instalaciones antiguas o rápidas): el PHP de todos los sitios se ejecuta como www-data o apache, y los archivos de todos los sitios pueden ser leídos (a menudo también modificados) por esa cuenta. Una web shell subida a través de un plugin vulnerable puede recorrer todo el directorio web, leer cada wp-config.php o archivo .env y plantar código en todos los sitios.
Usuarios separados: el PHP del sitio A se ejecuta como site_a y el del sitio B como site_b. Si el sitio A se ve comprometido, el código del atacante se ejecuta como site_a. Con los permisos adecuados, no puede leer los archivos ni las credenciales del sitio B, ni escribir malware en él.
Esto también facilita la limpieza. Cuando solo un sitio está afectado, usted restaura y limpia un sitio en lugar de auditar un servidor completo.

Cómo se ve un pool por sitio
Un archivo de pool típico (por ejemplo /etc/php/8.4/fpm/pool.d/site_a.conf en Debian o Ubuntu; las rutas varían según la distribución y el panel de control):
[site_a]
user = site_a
group = site_a
listen = /run/php/site_a.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
pm.max_requests = 500
request_terminate_timeout = 120s
security.limit_extensions = .php
php_admin_value[open_basedir] = /home/site_a:/tmp/site_a
php_admin_value[session.save_path] = /home/site_a/sessions
php_admin_value[upload_tmp_dir] = /tmp/site_a
php_admin_value[error_log] = /home/site_a/logs/php-error.log
php_admin_flag[log_errors] = on
php_admin_flag[display_errors] = off
Qué hace cada parte, en lenguaje sencillo:
- Usuario y grupo propios: el núcleo del aislamiento.
- Socket Unix propio con permisos
0660: solo la cuenta del servidor web puede comunicarse con este pool. El manual de PHP advierte que un endpoint FastCGI expuesto "permite la ejecución de código arbitrario" y recomienda un socket Unix cuando el servidor web está en el mismo equipo. Nunca exponga un puerto de FPM a internet. pm.max_children: limita cuántos procesos puede ejecutar un sitio, para que un pico de tráfico o un ataque contra un sitio no consuma toda la capacidad del servidor.pm.max_requests: recicla los procesos periódicamente, lo que mantiene bajo control las fugas de memoria del código de terceros.request_terminate_timeout: termina las solicitudes que tardan demasiado.security.limit_extensions = .php: FPM solo ejecuta archivos.php. El valor por defecto también permite.phar; el manual recomienda limitar FPM a.php.- Sesiones, temporales y logs separados: las sesiones en un
/tmpcompartido y accesible para todos pueden ser leídas por los usuarios de otros sitios, así que asigne a cada pool su propio directorio. php_admin_value: fija esos ajustes para que el propio código del sitio no pueda relajarlos.
Los permisos de archivo son la mitad del trabajo
Un usuario de PHP separado no sirve de nada si los archivos de todos los sitios son legibles por cualquiera. El esquema habitual:
- El directorio de cada sitio pertenece a su usuario y no es legible por otros usuarios (por ejemplo modo
0750o0710, dando acceso al servidor web mediante su grupo o una ACL específica). - Los archivos de configuración con secretos (
wp-config.php,.env) solo los puede leer el usuario del sitio, por ejemplo0600o0640. - El proceso de PHP no puede modificar los archivos de código salvo que la aplicación realmente lo necesite. Cuando sea posible, despliegue el código con un usuario distinto al que ejecuta PHP y dé a PHP permiso de escritura solo en las carpetas de subidas y caché.
Paneles de control como cPanel/WHM, Plesk y otros pueden crear pools de PHP-FPM por cuenta. Verifique que la opción esté realmente activada en todas las cuentas y que los sitios más antiguos se hayan migrado.
Conozca los límites
Los pools por sitio son una mejora práctica y sólida, pero no son un muro completo:
- OPcache compartido. El manual de PHP lo dice sin rodeos: los pools no son un mecanismo de seguridad porque no ofrecen una separación total; por ejemplo, todos los pools usan una sola instancia de OPcache. Ajustes como
opcache.validate_permissionyopcache.validate_root(para entornos con chroot) reducen los problemas de caché entre sitios; actívelos en servidores con varios usuarios. - Mismo kernel y mismo proceso maestro de PHP. Una vulnerabilidad en el kernel de Linux o en el propio PHP puede atravesar la barrera. Mantenga ambos con parches y en versiones con soporte.
- Mismo servidor de base de datos. Dé a cada sitio su propio usuario de base de datos, con acceso solo a su propia base.
- Existe un aislamiento más fuerte. Para sitios con niveles de riesgo muy distintos (un blog público en WordPress junto a un portal de facturación), los contenedores o las máquinas virtuales separadas ofrecen una separación mucho más firme.
Lista de verificación rápida
- Cada sitio tiene su propio pool de PHP-FPM y su usuario de Linux
- Cada pool escucha en su propio socket Unix,
0660, del grupo del servidor web -
pm.max_children,pm.max_requestsyrequest_terminate_timeoutdefinidos por sitio -
security.limit_extensions = .php - Sesiones, archivos temporales y logs separados por sitio
- Directorios no legibles por los usuarios de otros sitios; secretos en
0600/0640 - Un usuario de base de datos por sitio
- Validación de permisos de OPcache activada en servidores compartidos
Revisemos su servidor
Muchos servidores de pequeñas empresas del sur de Florida crecieron un sitio a la vez, y los sitios más antiguos a menudo siguen compartiendo un único usuario de PHP. Core Florida IT audita y reorganiza servidores web, cPanel/WHM y servidores Linux para empresas de Miami-Dade y el sur de Florida, con soporte remoto en todo el país. Solicite una evaluación de TI gratuita y revisaremos cómo están separados sus sitios.


