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 →

Prohibir la ejecución de PHP en las carpetas de subidas

Una webshell necesita dos cosas: una carpeta donde pueda escribirse y un servidor que la ejecute. Cómo quitarle lo segundo, y en toda carpeta con permiso de escritura, no solo en uploads.

Actualizado 6 min de lecturaRefuerzo y configuración

En resumen: para prohibir la ejecución de PHP en las subidas, averigüe primero todas las carpetas en las que PHP puede escribir y bloquee después en cada una la ejecución de scripts en la configuración del servidor, no mediante .htaccess. En Nginx, el bloqueo debe actuar antes del bloque general de PHP; en Apache pertenece al VirtualHost con AllowOverride None. security.limit_extensions = .php en el pool de FPM y una prueba con un archivo inofensivo completan el trabajo.

Por qué cuentan precisamente ahora las carpetas de subidas

Tras una vulnerabilidad crítica como PHP2Shell, los atacantes rara vez quieren ejecutar código solo una vez. Quieren volver, incluso cuando el servidor se parchee más tarde. El camino más sencillo para ello es una webshell: un pequeño archivo PHP, depositado en una carpeta con permiso de escritura y llamado después desde el navegador.

Un parche no elimina un archivo así. Quien estuvo conectado sin parchear a partir del 20 de octubre de 2026 no debería, por tanto, limitarse a actualizar, sino comprobar también si en algún lugar hay archivos PHP nuevos e impedir en el futuro que se ejecuten. Cómo interpretar los hallazgos lo describe Archivos PHP en la carpeta de subidas: interpretar bien los hallazgos.

El bloqueo de ejecución actúa sobre la segunda condición. No impide que se deposite un archivo, pero sí que el servidor lo trate como un programa.

Primero el inventario: ¿dónde puede escribir PHP?

La mayoría de las guías solo mencionan la carpeta de subidas. En la práctica, una aplicación PHP escribe en muchos más sitios. Cada uno de ellos que sea accesible a través del servidor web necesita el bloqueo.

Tipo de carpeta Contenido típico Por qué es escribible
Subidas, medios imágenes, PDF, adjuntos usuarios y redacción suben archivos
Caché páginas generadas, miniaturas la aplicación acelera el servicio
Temporal, importación, exportación archivos CSV, estados intermedios interfaces e intercambio de datos
Copias de seguridad en el webroot .zip, .sql las extensiones guardan copias «in situ»
Logs en el webroot logs de errores y de depuración aplicaciones mal configuradas

Qué carpetas son en su servidor lo muestra una búsqueda con los derechos del usuario de ejecución. No modifica nada:

# Todas las carpetas bajo /var/www en las que el usuario de PHP puede escribir
# (adapte el usuario: www-data, nginx, apache o el usuario propio del pool)
sudo -u www-data find /var/www -type d -writable 2>/dev/null | sort
# ¿Hay ya archivos PHP en esas carpetas?
sudo -u www-data find /var/www -type d -writable -print0 2>/dev/null \
  | xargs -0 -I{} find {} -maxdepth 1 -type f -iname "*.ph*" 2>/dev/null

Si la primera búsqueda devuelve todo el webroot, el código del programa pertenece al usuario de ejecución. Debería cambiarlo antes, como describe la guía Permisos de archivos en el servidor web. De lo contrario, no hay ninguna carpeta que no deba bloquearse.

Prohibir la ejecución de PHP en las subidas: Nginx

En Nginx decide el orden. Según la documentación de Nginx, las expresiones regulares se comprueban en el orden del archivo de configuración y gana la primera coincidencia. Si el bloque general location ~ \.php$ está antes de su bloqueo, el archivo se ejecuta. Una location de prefijo simple sin ^~ no protege por el mismo motivo.

Es seguro usar una expresión regular para todas las carpetas bloqueadas, colocada antes del bloque de PHP. Cubre también extensiones poco usadas:

server {
    # ... configuración básica ...

    # 1. Bloqueo para carpetas con permiso de escritura, DEBE ir antes del bloque de PHP
    location ~* ^/(uploads|media|cache|tmp|export)/.*\.(php\d?|phtml|phar|pht|phps)$ {
        deny all;
    }

    # 2. Bloque general de PHP: pasar solo archivos existentes
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/seite1.sock;
    }
}

Adapte los nombres de carpeta a su inventario. En aplicaciones con un único punto de entrada, el camino mediante una lista de permitidos es aún más fuerte; el patrón lo muestra Configurar Nginx para PHP de forma segura.

Apache y PHP-FPM: el bloqueo en el lugar correcto

En Apache, el bloqueo pertenece a la configuración del servidor o al VirtualHost. Un .htaccess en la carpeta de subidas no es adecuado, porque puede cambiarlo cualquiera que pueda escribir allí. Por eso, para todo el webroot rige AllowOverride None. Según la documentación de Apache, AllowOverride solo está permitido en bloques <Directory> sin expresiones regulares; por tanto, fíjelo en el webroot y coloque debajo el bloqueo con <DirectoryMatch>:

<Directory /var/www/seite1/public>
    AllowOverride None
    Require all granted
</Directory>

<DirectoryMatch "^/var/www/seite1/public/(uploads|media|cache|tmp|export)/">
    <FilesMatch "\.(php\d?|phtml|phar|pht|phps)$">
        Require all denied
    </FilesMatch>
</DirectoryMatch>

Si todavía se ejecuta mod_php, añada en el mismo bloque php_admin_flag engine off. En PHP-FPM actúa una segunda barrera en el pool: security.limit_extensions determina qué extensiones ejecuta FPM en absoluto. Según el manual de PHP, el valor por defecto es .php .phar; se recomienda limitarlo a .php.

; en el pool, p. ej. /etc/php/8.3/fpm/pool.d/seite1.conf
security.limit_extensions = .php

Probar con un archivo inofensivo

Solo una prueba muestra si el bloqueo funciona. Coloque un momento en cada carpeta bloqueada un archivo que se limite a mostrar un texto, llámelo a través del dominio y bórrelo después.

  1. Comprobar la configuración: nginx -t o apachectl configtest, y recargar.
  2. Crear el archivo de prueba, por ejemplo uploads/sperrtest.php con el contenido <?php echo 'ausgefuehrt';.
  3. Llamarlo con curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/uploads/sperrtest.php. Se espera 403.
  4. Si en su lugar aparece 200 y el texto «ausgefuehrt», el bloqueo no actúa. Revise la configuración y el orden.
  5. Borrar el archivo de prueba y repetir la prueba para cada carpeta y cada sitio web.
Importante

Si en el inventario encuentra ya archivos PHP desconocidos en carpetas con permiso de escritura, no borre nada precipitadamente. Asegure primero el estado y los logs, porque esos archivos son pruebas. Cómo proceder entonces lo describe Reconocer y eliminar webshells.

Cómo se construyen con seguridad las funciones de subida en el código, con comprobación de tipo y almacenamiento fuera del webroot, figura en la guía Implementar subidas de archivos seguras en PHP.

Conclusión: escribir sí, ejecutar no

El bloqueo de ejecución en carpetas con permiso de escritura es una de las medidas individuales más eficaces contra las puertas traseras permanentes. Cuesta pocas líneas de configuración y no rompe nada en aplicaciones bien construidas. Son decisivas tres cosas: un inventario completo en lugar de solo la carpeta de subidas evidente, el orden correcto en Nginx o el nivel correcto en Apache, y una prueba real con un archivo inofensivo.

Complementado con propietarios separados para el código del programa, security.limit_extensions en el pool y una búsqueda periódica de archivos PHP nuevos, se llega a un estado en el que un archivo depositado resulta molesto, pero apenas utilizable. Eso no sustituye ni la actualización ni la búsqueda de rastros, pero quita a los atacantes una de sus herramientas más importantes.

¿Cómo ayuda PHP SOS en concreto?

  • Inventario: averiguamos todas las carpetas con permiso de escritura por sitio web y comprobamos si ya contienen archivos PHP.
  • Fijar los bloqueos: registramos los bloqueos de ejecución en Nginx, Apache y PHP-FPM, en el orden correcto y sin depender de .htaccess.
  • Probar: comprobamos cada bloqueo con un archivo inofensivo y documentamos el resultado.
  • Limpiar: ante hallazgos, asumimos la conservación de rastros y la eliminación de malware.

Preguntas frecuentes

¿Basta un .htaccess en la carpeta de subidas para prohibir PHP?

No de forma fiable. Quien puede escribir en la carpeta puede también modificar o sustituir el .htaccess. El bloqueo pertenece a la configuración del servidor o al VirtualHost, y para el webroot debería regir AllowOverride None.

¿Qué carpetas debo bloquear?

Todas aquellas en las que el usuario de PHP puede escribir y que son accesibles a través del servidor web. Además de las carpetas de subidas, suelen ser directorios de caché, temporales, de importación, de exportación y de copias de seguridad. Averígüelos con una búsqueda como usuario de ejecución.

¿Rompe el bloqueo mi sitio web?

Por lo general no, porque en las carpetas de subidas y de caché de las aplicaciones bien hechas no hay scripts que se llamen directamente. Pero algunas extensiones sí dejan allí archivos PHP. Por eso, tras el cambio, pruebe las funciones más importantes.

¿Protege el bloqueo frente a PHP2Shell?

No. PHP2Shell es una vulnerabilidad del intérprete de PHP que solo se cierra con la actualización y el reinicio de todos los workers. Pero el bloqueo impide que un archivo depositado pueda llamarse después desde el navegador y usarse como puerta trasera permanente.

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