En resumen: open_basedir limita los directorios a los que puede acceder PHP. Por sí solo es solo una barrera adicional. El aislamiento eficaz por sitio web surge únicamente de la combinación: usuario del sistema propio, pool de PHP-FPM propio, carpeta temporal propia y accesos propios a la base de datos. Así, la intrusión en un sitio web queda limitada a ese sitio.
Por qué, tras PHP2Shell, el aislamiento decide el alcance del daño
En muchos servidores conviven varios sitios web: la web de la empresa, una tienda, una antigua página de campaña y, en las agencias, a menudo decenas de proyectos de clientes. Con frecuencia comparten un usuario del sistema y un proceso de PHP. Si uno de ellos se ve comprometido, todos los demás quedan expuestos.
PHP2Shell hace tangible este riesgo. Las vulnerabilidades CVE-2026-109618 y CVE-2026-109619 permiten ejecutar código malicioso sin autenticación, con independencia del CMS. Según informes de los medios, ya el 17/10/2026 hubo un primer intento de ataque; los parches llegaron el 21/10. La solución es la actualización. Pero que un ataque en el periodo anterior alcance a uno o a diez sitios web lo decide el aislamiento.
En la segunda vulnerabilidad se suma otra consideración: según el proyecto PHP, el estado interno de una petición puede persistir en un worker y ser utilizado por una petición posterior en el mismo worker. Los pools separados por sitio web hacen que los sitios no compartan workers en absoluto.
Qué consigue open_basedir
Con open_basedir define una lista de directorios. Las funciones de archivos de PHP, como fopen, include o file_get_contents, deniegan entonces el acceso a todo lo que quede fuera de esa lista. Así, un script introducido a escondidas no puede leer sin más los archivos de configuración del sitio web vecino.
Los límites son importantes: la comprobación se realiza dentro de PHP. Los programas que se inician fuera de PHP no están sujetos a ella, y los fallos del intérprete pueden eludirla. Por eso open_basedir debe ir siempre junto con funciones del sistema bloqueadas, como se describe en la guía Usar disable_functions correctamente, y sobre todo con una separación a nivel del sistema operativo.
Aislamiento por sitio web en cuatro capas
Una separación sólida se construye con varias capas. Cada una detiene lo que la anterior no consigue.
| Capa | Medida | Efecto |
|---|---|---|
| Sistema operativo | usuario del sistema propio para cada sitio web | Los archivos de otros sitios web no se pueden leer |
| Procesos de PHP | pool de PHP-FPM propio con socket propio | Sin worker compartido, ajustes propios |
| Configuración de PHP | open_basedir, carpeta tmp y de sesiones propias |
Barrera adicional, sin archivos temporales compartidos |
| Datos | usuario de base de datos propio con permisos solo sobre su propia base de datos | Sin acceso a datos de clientes ajenos |
Cuando se requiere una protección alta, por ejemplo en tiendas con procesamiento de pagos o en portales con datos de salud, se añaden contenedores o servidores separados.
Configurar open_basedir en el pool de PHP-FPM
El ejemplo siguiente muestra un pool para un sitio web con usuario propio seite1. Adapte las rutas y el número de versión a su sistema.
; /etc/php/8.3/fpm/pool.d/seite1.conf
[seite1]
user = seite1
group = seite1
listen = /run/php/seite1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
php_admin_value[open_basedir] = /var/www/seite1/:/var/www/seite1-tmp/
php_admin_value[upload_tmp_dir] = /var/www/seite1-tmp/
php_admin_value[sys_temp_dir] = /var/www/seite1-tmp/
php_admin_value[session.save_path] = /var/www/seite1-tmp/sessions
php_admin_value[error_log] = /var/log/php/seite1.error.log
php_admin_flag[session.upload_progress.enabled] = off
La última línea aplica de paso la medida transitoria de php.net contra PHP2Shell. Solo funciona desde el pool, php.ini o la configuración del servidor, no mediante ini_set() ni .user.ini. Las carpetas se crean con los permisos adecuados:
sudo useradd --system --home /var/www/seite1 --shell /usr/sbin/nologin seite1
sudo mkdir -p /var/www/seite1-tmp/sessions
sudo chown -R seite1:seite1 /var/www/seite1-tmp
sudo chmod 700 /var/www/seite1-tmp
Cómo deberían ser los permisos en el propio directorio web lo describe la guía Permisos de archivos en el servidor web.
Conectar el servidor web y comprobar el resultado
En Nginx, cada sitio web apunta a su propio socket. Así, ninguna petición acaba en el pool de otro sitio web.
server {
server_name seite1.example;
root /var/www/seite1/public;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/seite1.sock;
}
}
Encontrará más indicaciones sobre esta combinación en Proteger Nginx con PHP-FPM. Tras el reinicio, compruebe si los pools se ejecutan realmente separados:
sudo systemctl restart php8.3-fpm
# Cada pool debería ejecutarse con su propio usuario
ps -eo user,pid,cmd | grep "[p]hp-fpm: pool"
Después, pruebe todas las funciones del sitio web: subidas, procesamiento de imágenes, cachés, tareas programadas. Los mensajes de error con la indicación open_basedir restriction en el registro de errores muestran qué ruta falta todavía. Añada solo rutas que la aplicación realmente necesite y nunca todo el sistema de archivos.
Las agencias con muchos sitios de clientes deberían mantener la configuración del pool como plantilla. Así, cada sitio nuevo recibe la separación automáticamente, en lugar de tener que añadirla después. Los antecedentes de este caso están en Socio de seguridad para agencias.
Obstáculos típicos en la migración
La migración a pools separados es planificable, pero tiene algunas trampas recurrentes. La más frecuente afecta a las bibliotecas compartidas: si, por ejemplo, un framework o una carpeta con fuentes tipográficas está fuera del directorio del sitio web, esa ruta debe incluirse en open_basedir con acceso de lectura. Es mejor traer esas dependencias al propio proyecto.
La segunda trampa son las sesiones. Si cambia el lugar de almacenamiento, todos los visitantes quedan desconectados una vez y se pierden los carritos de la compra en curso. Planifique por eso la migración en una franja tranquila y anúnciela internamente.
En tercer lugar, las tareas programadas suelen ejecutarse por la línea de comandos y, por tanto, con un usuario distinto al del sitio web. Revise las tareas cron y las colas y haga que se ejecuten con el nuevo usuario del sitio web; de lo contrario, se crean archivos con propietario incorrecto que PHP después ya no puede modificar.
Por último, cada pool debería tener sus propios límites de procesos y memoria. Así, un sitio web que esté bajo carga o sufra un ataque no puede arrastrar a los demás.
¿Cómo ayuda PHP SOS en concreto?
- Averiguamos qué sitios web comparten hoy usuarios, procesos y bases de datos.
- Configuramos para cada sitio web usuarios, pools, carpetas temporales y accesos a la base de datos propios, sin modificar las aplicaciones.
- Probamos cada sitio web tras la migración y corregimos de forma específica las rutas que faltan.
- Documentamos la separación como plantilla para proyectos futuros.
Cómo encaja en un concepto global lo describe el servicio Refuerzo de servidores PHP.
Preguntas frecuentes
¿Basta open_basedir para separar los sitios web entre sí?
No. open_basedir es una barrera adicional dentro de PHP, no una separación sólida. El aislamiento solo es fiable con usuarios del sistema y pools propios para cada sitio web y, si se necesita mayor protección, con contenedores o servidores separados.
¿Por qué dejan de funcionar las subidas después de establecer open_basedir?
Normalmente falta en la lista la carpeta de archivos temporales. Cree una carpeta tmp propia para cada sitio web, inclúyala en open_basedir y apunte upload_tmp_dir y sys_temp_dir a esa carpeta.
¿Puede una aplicación anular open_basedir por sí misma?
En tiempo de ejecución, una aplicación solo puede restringir más el ajuste, no relajarlo. Aun así, establezca open_basedir mediante php_admin_value en el pool, para que el valor quede fijado de forma inequívoca.
¿Es posible el aislamiento también en un hosting compartido?
Eso depende del proveedor. Muchas ofertas separan a los clientes mediante usuarios y procesos de PHP propios. Pregunte cómo se aplica la separación, sobre todo si varios de sus sitios web están en un mismo paquete.