En pocas palabras: Bloqueá la ejecución de PHP en wp-content/uploads con un bloque <FilesMatch> y Require all denied en Apache 2.4, o un location que niegue PHP en Nginx: así el servidor trata cualquier .php subido como texto plano y no como script, cortando la vía más común para instalar un webshell.

Bloquear la ejecución de PHP en wp-content/uploads es la barrera más simple contra webshells en WordPress: un archivo .htaccess en Apache o un bloque location en Nginx le dice al servidor que trate cualquier archivo .php de esa carpeta como texto plano, no como script, aunque el atacante ya lo haya subido disfrazado de imagen.

wp-content/uploads es el directorio donde WordPress guarda las imágenes, videos y documentos que subís desde el editor o desde cualquier plugin con formulario de carga. Por diseño no debería ejecutar código: bloquear la ejecución de PHP en uploads significa configurar el servidor para que interprete cualquier archivo .php de esa carpeta como contenido estático, no como script, cortando así la vía más común para instalar un webshell.

En 30 segundos

  • Bloquear PHP en uploads corta la ejecución de un webshell aunque el archivo ya esté guardado en el servidor.
  • En Apache se hace con un bloque <FilesMatch> y Deny from all (o Require all denied en 2.4+) dentro de un .htaccess en wp-content/uploads.
  • En Nginx se agrega un bloque location que deniega cualquier .php bajo /uploads/ dentro del server block.
  • Wordfence tenía un checkbox para esto («Disable PHP execution in Uploads directory»), pero lo sacó en la versión 7.1.16; hoy hay que aplicarlo a mano.
  • Antes de bloquear conviene escanear la carpeta buscando webshells ya subidos, porque el bloqueo no los elimina, solo evita que se ejecuten de nuevo.

¿Cómo llega un webshell a la carpeta wp-content/uploads?

Un webshell llega a wp-content/uploads casi siempre por un plugin con un formulario de carga de archivos mal validado, no por el uploader nativo de WordPress. El uploader de medios integrado filtra extensiones por defecto, pero un formulario de contacto, un importador de productos o un plugin de currículums que acepta «cualquier documento» puede saltarse ese filtro.

Ponele que instalaste un plugin de postulación laboral para que la gente suba su CV: el atacante manda un archivo llamado cv.php.jpg, o directamente cv.php con el content-type falseado como imagen, y si el plugin no revalida la extensión real en el servidor, ese archivo queda guardado en uploads. A partir de ahí, con solo pedir esa URL desde el navegador, el atacante ejecuta comandos en tu servidor como si tuviera una terminal (spoiler: no hace falta que sea sofisticado para que funcione). Complementá con la comparativa entre Patchstack y Wordfence en detalle, o mirá la guía de Wordfence.

También pasa con temas que incluyen archivos de forma dinámica sin sanitizar la ruta, o con importadores XML/CSV que aceptan adjuntos. El patrón se repite: algo en el sitio permite escribir un archivo dentro de una carpeta con permisos de escritura del servidor.

¿Por qué wp-content/uploads no debería poder ejecutar código PHP?

wp-content/uploads no debería ejecutar PHP porque WordPress la diseñó como depósito de archivos estáticos, no como carpeta de lógica de servidor. Ahí van imágenes, PDFs, videos y audio, nunca código que el servidor tenga que interpretar.

Por eso, cualquier archivo .php ejecutándose desde esa carpeta es, en la práctica, una señal de compromiso. No hay un escenario legítimo en el que un plugin bien construido necesite correr PHP desde uploads: la lógica va en wp-content/plugins o wp-content/themes, carpetas que además suelen tener controles de integridad más estrictos. Bloquear la ejecución ahí no rompe funcionalidad real porque no hay funcionalidad real que dependa de eso.

¿Wordfence bloquea la ejecución de PHP en uploads automáticamente?

No, hoy Wordfence no bloquea la ejecución de PHP en uploads de forma automática. Durante un tiempo el plugin tuvo, dentro de las opciones del dashboard, un checkbox llamado «Disable PHP execution in Uploads directory» que generaba un .htaccess con reglas para mod_php5, mod_php7 y mod_php.

Ese checkbox se retiró en la versión 7.1.16, lanzada en 2017, en el marco de ajustes vinculados al cumplimiento del RGPD europeo. ¿Y qué significa esto para vos? Que si instalaste Wordfence después de esa versión, no tenés ese bloqueo activado por defecto, aunque lo hayas tenido en una instalación vieja. Wordfence sigue siendo útil para escanear malware y detectar webshells ya subidos, pero la prevención vía bloqueo de ejecución PHP en uploads hoy es una tarea manual, a nivel de servidor.

¿Cómo bloquear PHP en wp-content/uploads con .htaccess en Apache?

En Apache, bloqueás PHP en wp-content/uploads creando un archivo .htaccess dentro de esa carpeta con una regla FilesMatch que deniega el acceso a cualquier archivo .php. La sintaxis cambia según la versión de Apache que tenga tu hosting. Para más detalles técnicos, mirá qué firewall bloquea más ataques reales.

Para Apache 2.4 en adelante (la mayoría de los hostings actuales):

  • Creá el archivo: wp-content/uploads/.htaccess (si no existe, lo generás vos con cualquier editor de texto).
  • Pegá el bloque: <FilesMatch "\.php$"> seguido de Require all denied y </FilesMatch>.
  • Subilo por FTP o SFTP a la carpeta uploads de tu instalación.

Si tu hosting todavía corre Apache 2.2 (cada vez menos común), la directiva es distinta: <FilesMatch "\.php$">, Order allow,deny, Deny from all, </FilesMatch>. Fijate la versión de Apache en el panel de tu hosting antes de elegir la sintaxis, porque la regla vieja simplemente no hace nada en un servidor 2.4.

¿Cómo bloquear la ejecución de PHP en uploads en servidores Nginx?

En Nginx bloqueás PHP en uploads agregando un bloque location dentro del server block del sitio, ya que Nginx no lee archivos .htaccess. La regla típica es algo como location ~* /(?:uploads|files).*\.php$ { deny all; }, ubicada antes del bloque que procesa PHP con fastcgi_pass.

Acá el tema es que un error de sintaxis en la configuración de Nginx no te tira un error amigable en el navegador, directamente te puede tumbar el sitio entero (o el servidor completo, si administrás varios dominios ahí). Por eso, antes de recargar el servicio, corré nginx -t para validar la sintaxis, y recién después ejecutá systemctl reload nginx. Si no tenés acceso directo al archivo de configuración, porque tu hosting lo administra por vos, pedile a soporte que aplique esa regla o migrá a un plan donde tengas ese control. En donweb.com el hosting administrado incluye ese tipo de ajustes de servidor como parte del soporte técnico.

ServidorDónde va la reglaRegla claveRequiere reiniciar
Apache.htaccess dentro de wp-content/uploadsFilesMatch + Require all denied (2.4+)No, aplica al instante
Nginxserver block del sitio (nginx.conf o vhost)location ~* con deny allSí, nginx -t y reload

¿Qué otras carpetas de WordPress conviene proteger de la misma forma?

Además de uploads, conviene aplicar el mismo bloqueo a cualquier carpeta con permisos de escritura del servidor donde no debería ejecutarse código.

  • wp-content/cache: la usan los plugins de caché para guardar versiones estáticas de las páginas; si un atacante logra escribir ahí, el bloqueo evita que ese archivo corra como script.
  • Carpetas de backup de plugins: plugins como los de respaldo suelen crear directorios con nombres predecibles; si quedan expuestos, son un blanco fácil.
  • Directorios de subida específicos de plugins: por ejemplo woocommerce_uploads o carpetas de adjuntos de formularios, que a veces viven fuera de wp-content/uploads.
  • wp-includes: no debería tener archivos ejecutables agregados después de la instalación; cualquier .php nuevo ahí es sospechoso.

La lógica es siempre la misma: si el servidor puede escribir en una carpeta, esa carpeta es un vector potencial. Cuantas menos carpetas puedan ejecutar PHP sin necesitarlo, menos superficie de ataque. Ya lo cubrimos antes en elegir entre Wordfence y Sucuri.

¿Cómo verificar que el bloqueo funciona sin romper plugins legítimos?

Verificás que el bloqueo funciona subiendo un archivo .php de prueba a wp-content/uploads (por FTP, no por el uploader de medios, que lo va a rechazar) y pidiendo esa URL desde el navegador. Si la regla está bien aplicada, tenés que ver un error 403 Forbidden.

Antes de aplicar esto en producción, revisá si algún plugin de caché, de generación de PDFs o de CDN necesita ejecutar algo desde esa carpeta específica. Es poco común, pero existe: algunos generadores de facturas o de reportes escriben scripts temporales en subcarpetas de uploads. Si tenés dudas, probá primero en un entorno de staging, subí el .htaccess o la regla de Nginx, y navegá el sitio completo revisando el panel de administración, el checkout si tenés WooCommerce, y cualquier funcionalidad que genere archivos dinámicamente.

¿Cómo detectar si ya subieron un webshell antes de aplicar el bloqueo?

Detectás un webshell ya subido escaneando el sitio con una herramienta de malware como Wordfence y revisando manualmente los archivos .php dentro de wp-content/uploads, donde no debería haber ninguno.

Además del escaneo, revisá los logs de acceso del servidor buscando pedidos a rutas dentro de /uploads/ que terminen en .php, sobre todo si vienen con parámetros raros en la URL o con métodos POST repetidos desde la misma IP. ¿Encontraste algo? Aislá el archivo primero, después lo eliminás, y rotá las credenciales de WordPress, FTP y base de datos, porque si hubo un webshell activo, probablemente el atacante también levantó esas contraseñas. Bloquear la ejecución de PHP en uploads después de esto evita que vuelva a pasar, pero no reemplaza la limpieza del archivo malicioso que ya está ahí. Relacionado: cuál limpia mejor un sitio infectado.

Errores comunes al bloquear PHP en uploads

  • Aplicar la regla de Apache 2.2 en un servidor 2.4: Deny from all sin Order allow,deny no hace nada en Apache 2.4+; ahí la directiva correcta es Require all denied.
  • No revisar la sintaxis en Nginx antes de recargar: un bloque location mal cerrado puede tumbar el sitio entero hasta que alguien lo corrija; siempre nginx -t primero.
  • Confiar en que Wordfence ya lo bloquea: desde la versión 7.1.16 ese checkbox no existe más, así que si nunca tocaste el .htaccess de uploads, lo más probable es que no tengas ninguna protección activa ahí.
  • Bloquear solo uploads y dejar el resto abierto: si tenés wp-content/cache o carpetas de backup con los mismos permisos de escritura, un atacante simplemente sube el webshell ahí en vez de en uploads.
  • No limpiar el webshell que ya estaba antes del bloqueo: la regla evita ejecuciones nuevas, pero un archivo malicioso ya guardado sigue ahí hasta que lo borrás.

Preguntas Frecuentes

¿Cómo bloquear la ejecución de PHP en wp-content/uploads?

Creando un archivo .htaccess dentro de wp-content/uploads con un bloque FilesMatch que deniegue el acceso a archivos .php (en Apache), o agregando un bloque location con deny all en la configuración del sitio (en Nginx). En ambos casos el servidor deja de interpretar cualquier .php subido a esa carpeta.

¿Por qué es importante bloquear PHP en la carpeta de uploads de WordPress?

Porque uploads es la carpeta con permisos de escritura más expuesta del sitio, ya que cualquier formulario de carga de archivos escribe ahí. Bloquear la ejecución de PHP corta el paso final de un ataque de subida de webshell, aunque el atacante logre subir el archivo malicioso.

¿Wordfence bloquea automáticamente la ejecución de PHP en uploads?

No. Wordfence tuvo esa opción como checkbox en el dashboard, pero la retiró en la versión 7.1.16, lanzada en 2017, por ajustes vinculados al RGPD. Hoy hay que aplicar el bloqueo manualmente vía .htaccess (Apache) o configuración de servidor (Nginx).

¿Qué es un webshell y cómo llega a un sitio WordPress?

Un webshell es un script malicioso, generalmente en PHP, que le da a un atacante control remoto sobre el servidor a través de una URL. Llega casi siempre por plugins de subida de archivos mal validados, como formularios de contacto o importadores, que aceptan archivos con extensión .php disfrazados de otro tipo de documento.

¿Cómo bloquear PHP en uploads si mi hosting usa Nginx?

Agregando un bloque location ~* /(?:uploads|files).*\.php$ { deny all; } dentro del server block del sitio en el archivo de configuración de Nginx, validando la sintaxis con nginx -t antes de recargar el servicio. Si tu hosting es administrado, pedile a soporte que aplique esa regla por vos.

Conclusión

Bloquear la ejecución de PHP en uploads es uno de esos ajustes que cuestan cinco minutos y cierran una de las vías de compromiso más usadas contra WordPress. El checkbox de Wordfence que lo hacía automático desapareció en la versión 7.1.16, lanzada en 2017, así que hoy la responsabilidad quedó del lado del administrador del sitio, vía .htaccess en Apache o un bloque location en Nginx.

Si administrás un sitio con formularios de carga de archivos, WooCommerce, o cualquier plugin que reciba adjuntos de usuarios, aplicá esta regla ahora, verificá con un archivo de prueba que devuelva 403, y corré un escaneo de malware antes para confirmar que no hay ningún webshell esperando. Después, sumá el mismo criterio a wp-content/cache y a cualquier otra carpeta con permisos de escritura.

Fuentes

Categorizado en: