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.
- Comprobar la configuración:
nginx -toapachectl configtest, y recargar. - Crear el archivo de prueba, por ejemplo
uploads/sperrtest.phpcon el contenido<?php echo 'ausgefuehrt';. - Llamarlo con
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/uploads/sperrtest.php. Se espera403. - Si en su lugar aparece
200y el texto «ausgefuehrt», el bloqueo no actúa. Revise la configuración y el orden. - Borrar el archivo de prueba y repetir la prueba para cada carpeta y cada sitio web.
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.