En resumen: fije session.upload_progress.enabled=0 en el php.ini, en el pool de PHP-FPM o en la configuración del servidor Apache, nunca mediante ini_set() ni .user.ini, y reinicie después todos los workers de PHP. La medida solo es permanente cuando el valor está bloqueado frente a una reactivación, rige en todos los modos de funcionamiento y en todas las versiones de PHP instaladas, y se vuelve a comprobar tras las actualizaciones.
Un ajuste pequeño con doble efecto
Pocas veces un solo ajuste de PHP ha recibido tanta atención. Cuando php.net publicó el 21 de octubre de 2026 los parches contra PHP2Shell, session.upload_progress.enabled=0 figuraba en la lista de medidas transitorias. Dos días después llegó el aviso sobre CVE-2026-115560, y la contramedida recomendada era el mismo ajuste.
Muchos administradores lo fijaron rápidamente en esos días. Si rige en todas partes es otra cuestión. Las lagunas típicas se enumeran enseguida: el valor figura solo en la configuración de CLI, se olvidó una segunda versión de PHP o un panel de hosting sobrescribió el archivo en el siguiente guardado. Este artículo muestra cómo aplicar la medida de modo que siga vigente también dentro de tres meses.
Importante de antemano: desactivar la función no sustituye a la actualización de PHP. Según informes especializados, session.upload_progress.enabled=0 por sí solo no cierra las dos vulnerabilidades de PHP2Shell. A qué afecta exactamente CVE-2026-115560 lo explica la página CVE-2026-115560: aviso sobre session upload progress.
Qué hace la función y quién la necesita
Session upload progress escribe de forma continua en la sesión el progreso durante la subida de un archivo, para que una aplicación pueda mostrar una barra de progreso. En PHP, la función está activada por defecto. El propio manual de PHP advierte de que, con ello, los nombres de archivo y de campo proporcionados por el remitente acaban sin comprobar en el almacenamiento de sesión, y recomienda desactivar la función si la aplicación no la usa.
Si su aplicación la usa, lo averigua con una búsqueda de solo lectura en el código:
# Busca usos de la función de progreso en el código de la aplicación
grep -rn --include="*.php" -e "upload_progress" -e "PHP_SESSION_UPLOAD_PROGRESS" /var/www/
Si la búsqueda no encuentra nada, puede desactivar la función sin consecuencias perceptibles. Si hay coincidencias, la aplicación solo pierde la indicación de progreso, no la subida. Los formularios modernos suelen medir el progreso en el navegador de todos modos.
Fijar session.upload_progress.enabled en el lugar correcto
Según el manual de PHP, el ajuste tiene el modo INI_PERDIR. Esto significa que puede fijarse en el php.ini, en la configuración del servidor, en .htaccess (solo mod_php) y en .user.ini (solo FPM y CGI), pero no en tiempo de ejecución. php.net excluye expresamente .user.ini como lugar para la medida. La tabla siguiente muestra los lugares adecuados según el modo de funcionamiento.
| Modo de funcionamiento | Lugar recomendado | ¿Bloquea la sobrescritura? | Reiniciar después |
|---|---|---|---|
| PHP-FPM | archivo del pool con php_admin_flag |
sí | cada servicio FPM |
| PHP-FPM | php.ini de la SAPI de FPM | no, .user.ini podría sobrescribirlo |
cada servicio FPM |
| Apache con mod_php | VirtualHost o configuración del servidor con php_admin_flag |
sí | Apache por completo |
| php-cgi, LiteSpeed, FrankenPHP | php.ini del entorno de ejecución correspondiente | comprobar según la estructura | todos los procesos del entorno |
| Imagen de Docker | archivo propio en /usr/local/etc/php/conf.d/ durante la construcción |
sí, si no se permite override | recrear el contenedor |
Las dos variantes robustas tienen este aspecto:
; PHP-FPM, p. ej. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_flag[session.upload_progress.enabled] = off
# Apache con mod_php, en el VirtualHost o para todo el servidor
<IfModule php_module>
php_admin_flag session.upload_progress.enabled off
</IfModule>
Además, merece la pena la entrada en el php.ini de cada SAPI web como valor base. Así el ajuste rige también para los pools nuevos que alguien cree más adelante. Por qué php_admin_flag marca la diferencia lo describe el artículo php_admin_value: bloquear ajustes frente a la sobrescritura.
Comprobar la eficacia en el contexto web
La línea de comandos suele leer un php.ini distinto del que usa el servidor web. Por eso un php -i demuestra poco. Tampoco php-fpm8.3 -i muestra más que los valores del php.ini, no las entradas de los pools. Solo es fiable una consulta que se ejecute realmente a través del pool.
Proceda así:
- Reinicie todos los servicios de PHP, por ejemplo
systemctl restart php8.3-fpm, o Apache con mod_php. Sin reinicio sigue en uso el valor antiguo. - Coloque en el webroot un breve archivo de prueba con nombre aleatorio que solo muestre el valor actual.
- Llame una vez al archivo a través del dominio real y anote el resultado.
- Borre el archivo de inmediato y repita el paso para cada sitio web y cada pool.
<?php
// Archivo de prueba: déjelo solo un momento y bórrelo enseguida
header('Content-Type: text/plain');
echo 'session.upload_progress.enabled = ', var_export(ini_get('session.upload_progress.enabled'), true), "\n";
echo 'PHP ', PHP_VERSION, ' / SAPI ', PHP_SAPI, "\n";
Se espera un valor vacío o '0'. Si aparece '1', su ajuste no rige en ese pool. Tras una actualización de PHP, compruebe además que todos los workers se han reiniciado, como se describe en Reiniciar PHP-FPM y todos los workers.
No deje nunca archivos de prueba. Revelan versiones y configuración y son un objetivo habitual de los escaneos automatizados.
Para que el ajuste se mantenga
La mayoría de las recaídas no las provocan los atacantes, sino la operativa. Se copia un pool nuevo de una plantilla antigua, una actualización de paquete crea un php.ini nuevo o alguien cambia la versión de PHP en el panel de hosting. Contra estas recaídas ayudan unos hábitos firmes.
- Cubrir todas las versiones: en muchos servidores se ejecutan varias versiones de PHP en paralelo. Cada una tiene sus propias carpetas de configuración y necesita la entrada.
- Adaptar las plantillas: las plantillas de pools, roles de Ansible, Dockerfiles y plantillas de panel incluyen el valor, para que los sitios web nuevos lo hereden automáticamente.
- Usar los ajustes del panel: en los paneles de hosting, el valor pertenece a las directivas adicionales previstas por el panel; de lo contrario, el siguiente cambio sobrescribe el trabajo manual.
- Comprobar tras las actualizaciones: la comprobación de la sección anterior forma parte de la lista de verificación de cada actualización de PHP.
- Documentar: anote la fecha, el servidor, los pools y el resultado. Eso ayuda más adelante frente a la dirección, la aseguradora o los auditores.
Quien no usa .user.ini en absoluto puede desactivar por completo la lectura de esos archivos con user_ini.filename = en el php.ini. Pero eso afecta a todos los ajustes que las aplicaciones guardan allí. Por eso pruebe antes este paso, sobre todo en CMS que escriben sus propios archivos .user.ini.
Las demás medidas transitorias de php.net, como los límites de post_max_size y el límite de memoria mediante cgroup, figuran reunidas en Medidas inmediatas si todavía no puede parchear. Cómo ordena PHP en general sus niveles de configuración lo explica El php.ini explicado.
¿Cómo ayuda PHP SOS en concreto?
- Inventariar: averiguamos todas las versiones de PHP, modos de funcionamiento y pools de sus servidores y leemos los valores efectivos en el contexto web.
- Fijarlo bien: registramos el ajuste bloqueado en los pools y en la configuración del servidor y reiniciamos todos los workers de forma controlada.
- Probar el funcionamiento: tras el cambio revisamos los formularios de subida y la gestión de medios.
- Evitar recaídas: adaptamos plantillas, despliegues y ajustes del panel y documentamos el estado en el marco del refuerzo de servidores.
Preguntas frecuentes
¿Por qué session.upload_progress.enabled=0 no surte efecto mediante ini_set()?
Según el manual de PHP, el ajuste pertenece al modo INI_PERDIR. Por eso no puede cambiarse en tiempo de ejecución con ini_set(). php.net indica como lugares correctos el php.ini, el pool de FPM o la configuración del servidor, y excluye expresamente .user.ini.
¿Siguen funcionando las subidas de archivos tras desactivarlo?
Sí. Solo se desactiva el aviso de progreso que PHP escribe en la sesión durante una subida. La subida en sí continúa. Solo se ven afectadas las aplicaciones que construyen su barra de progreso precisamente con esta función de PHP.
¿Puede una aplicación volver a activar la función?
En principio sí, mediante un archivo .htaccess con mod_php o un .user.ini con PHP-FPM, mientras el valor no esté bloqueado. Con php_admin_flag en el pool o en la configuración de Apache lo impide.
¿Debo restablecer el valor tras la próxima actualización de PHP?
No, no es necesario. La mayoría de las aplicaciones no necesitan la función, y el propio manual de PHP aconseja desactivarla en esos casos. Tras cada actualización, compruebe solamente que el valor sigue vigente.