Guía PathSentinel

Cómo limpiar un WordPress hackeado paso a paso

Procedimiento completo para limpiar a mano un WordPress infectado, paso a paso y en orden, con los puntos donde casi todo el mundo se deja algo dentro.

Si has llegado hasta aquí es porque ya sabes o sospechas que tu WordPress está infectado: redirige a sitios que no son tuyos, Google lo ha marcado, han aparecido archivos que tú no subiste o el hosting te ha mandado un aviso. Abajo tienes el procedimiento de limpieza manual completo, en el orden en el que hay que hacerlo. No necesitas ser programador, pero sí necesitas tres accesos: el panel del hosting (cPanel, Plesk o el que uses), acceso por SFTP y acceso a la base de datos por phpMyAdmin. Si te falta alguno, consíguelo antes de empezar; sin ellos vas a limpiar solo la mitad.

Una advertencia para que no te lleves un chasco a mitad de camino: limpiar una web tiene dos partes. Una es quitar lo que se ve (la redirección, el spam, la página falsa). La otra es quitar lo que el atacante dejó para volver a entrar, y es la que decide si dentro de dos semanas estás otra vez igual. Si aún no tienes claro qué te está pasando, identifica primero el síntoma con la guía general sobre qué hacer cuando tu web está hackeada.

Lo esencial

  • Antes de empezar necesitas tres accesos: el panel del hosting, SFTP y la base de datos por phpMyAdmin; sin ellos solo limpiarás la mitad.
  • Lo primero no es borrar sino congelar el estado infectado (archivos, base de datos y registros) y poner la web en mantenimiento.
  • Lo que se puede sustituir no se limpia: núcleo, plugins y temas se reinstalan desde su origen oficial; wp-content y la base de datos se revisan a mano.
  • Cierra los accesos: comprueba que no haya administradores ocultos, cambia las contraseñas en orden, regenera las claves de seguridad y revisa las tareas programadas y todos los .htaccess.
  • Verifica desde fuera antes de pedir la revisión y vigila quince días; si es una tienda, ya volvió una vez o hay varias webs en el mismo hosting, para y busca ayuda.

¿Sospechas que tu web está hackeada? Compruébalo gratis en un minuto.

🔍 Analizar mi web ahora

Antes de empezar: copia de seguridad forense y modo mantenimiento

Lo primero no es borrar nada, es congelar el estado actual aunque esté infectado. Esa copia (backup) sirve para volver atrás si rompes algo, comparar después qué había, y tener pruebas si esto acaba en una reclamación al hosting o en una notificación por brecha de datos.

  • Archivos: en el administrador de archivos del hosting, comprime la carpeta public_html (o httpdocs en Plesk) entera y descárgala.
  • Base de datos: phpMyAdmin, selecciona la base de datos, pestaña Exportar, método rápido, formato SQL, y descarga el archivo.
  • Registros: descarga los logs de acceso y de error ahora. El hosting los rota y en pocos días desaparecen. Suelen estar en /home/usuario/logs/, en ~/access-logs/ o en la sección de métricas del panel. Sin ellos no vas a poder saber por dónde entraron.

Guarda esas tres cosas fuera del servidor, en una carpeta con la fecha en el nombre, y no las restaures nunca sobre la web: son material de consulta, no una copia de trabajo.

Después, pon la web en mantenimiento. Mientras siga sirviendo malware a las visitas, Google no te va a quitar el aviso. Lo más limpio es renombrar el index.php y subir un index.html con un aviso, o restringir el acceso por IP en el .htaccess mientras trabajas.

Si tienes una copia anterior al ataque, la tentación de restaurarla y cerrar el asunto es enorme. Es legítimo, pero incompleto: restaurar una copia no cierra la puerta por la que entraron, y si la infección es más antigua de lo que crees, estarás restaurando una web ya tocada.

Sustituir núcleo, temas y plugins por copias oficiales

No intentes desinfectar archivo por archivo lo que puedes reemplazar entero. Es más rápido y mucho más seguro.

El núcleo de WordPress

Descarga de es.wordpress.org la misma versión mayor que tienes instalada. Borra por completo wp-admin/ y wp-includes/ y sube las nuevas. En la raíz, sustituye todos los wp-*.php menos wp-config.php. Después mira qué ha quedado suelto: cualquier archivo PHP que no venga en el paquete oficial es sospechoso por definición. Los nombres típicos imitan a los de verdad (wp-conflg.php, wp-cache-old.php) o son cadenas aleatorias de ocho caracteres.

Plugins y temas

Borra la carpeta completa de cada plugin y reinstálalo desde el repositorio oficial o desde el ZIP del autor. No descomprimas encima: si copias archivos nuevos sobre los viejos, el que sobra se queda ahí tan tranquilo. Y aprovecha para dos decisiones incómodas: un plugin retirado del repositorio oficial no se reinstala, se sustituye; y un plugin o tema de pago descargado de una web de «nulled» no se limpia, se borra y se compra. Ahí estaba el problema en un porcentaje incómodo de los casos.

De los temas, deja solo el que usas y su tema hijo. Los inactivos también contienen archivos PHP a los que se puede llamar directamente por URL.

Por último, abre wp-config.php y léelo entero. Debe empezar por <?php sin ningún espacio delante y no debe contener eval(, base64_decode( ni ningún include que apunte a un archivo con nombre raro.

wp-content: dónde se esconde lo que no se puede sustituir

Aquí está tu contenido, así que aquí no se puede borrar y volver a subir. Hay que mirar.

  • wp-content/uploads/ no debe contener archivos PHP. Nunca, bajo ningún concepto. Si tienes SSH: find wp-content/uploads -type f ( -name "*.php" -o -name "*.phtml" -o -name "*.php7" ). Si no tienes SSH, usa el buscador del administrador de archivos del hosting sobre esa carpeta.
  • Un .htaccess dentro de uploads que active la ejecución de PHP es muy mala señal. Ahí solo debería haber, como mucho, uno que la desactive.
  • Carpetas con años que no existen dentro de uploads (un 2029/) o nombres genéricos tipo uploads/tmp/.
  • wp-content/mu-plugins/. Lo que se pone ahí se ejecuta siempre y no aparece en la lista de plugins del panel. Si existe y tú no la creaste, revisa cada archivo.
  • Carpetas de caché y de copias (wp-content/cache/, upgrade/, backups/). Se pueden vaciar sin miedo y son un escondite habitual.

Es la parte que más paciencia requiere y donde más gente se deja algo dentro. Si quieres el detalle de qué patrones buscar dentro de los archivos, tienes el método completo en la guía sobre cómo buscar puertas traseras en tu web.

Limpiar la base de datos sin perder contenido

Antes de tocar nada aquí, exporta otra vez la base de datos. Y ten en cuenta que el prefijo de las tablas puede no ser wp_: míralo en wp-config.php, línea $table_prefix, y ajusta las consultas.

Primero, en wp_options, comprueba que siteurl y home apuntan a tu dominio y nada más. Después busca opciones enormes que se cargan en cada visita:

SELECT option_name, LENGTH(option_value) AS tam
FROM wp_options
WHERE autoload = 'yes'
ORDER BY tam DESC
LIMIT 20;

Lo normal es ver ahí opciones de tus plugins. Una opción con nombre aleatorio y cientos de kilobytes de contenido ilegible, no. Después, busca inyecciones en el contenido publicado:

SELECT ID, post_title, post_status
FROM wp_posts
WHERE post_content LIKE '%<script%'
   OR post_content LIKE '%eval(%'
   OR post_content LIKE '%base64_decode%';

Ojo: si usas bloques de código, formularios incrustados o vídeos, vas a tener resultados legítimos. Revisa uno a uno y edita a mano. Un buscar y reemplazar masivo a ciegas sobre wp_posts es la forma más eficiente de convertir una web infectada en una web infectada y además rota. Mira también si hay tablas que no pertenecen a nada de lo que tienes instalado.

Usuarios, claves de seguridad y sesiones abiertas

En Usuarios > Todos, filtra por administradores y comprueba que los conoces a todos. Pero no te fíes solo del panel: hay inyecciones que ocultan un usuario del listado. Compáralo con la realidad:

SELECT ID, user_login, user_email, user_registered
FROM wp_users
ORDER BY user_registered DESC;

Si esa consulta devuelve más cuentas de las que ves en el panel, tienes un problema activo, no un resto. Borra las que no reconozcas desde phpMyAdmin y limpia sus filas en wp_usermeta.

Después cambia contraseñas en este orden: administradores de WordPress, cuentas FTP/SFTP (elimina las que no uses en vez de cambiarlas), panel del hosting, usuario de la base de datos (actualiza wp-config.php a la vez) y el correo asociado al administrador.

Y ahora la medida que más rinde y que casi nadie aplica: regenera las claves de seguridad. Entra en https://api.wordpress.org/secret-key/1.1/salt/ y sustituye con lo que te devuelve las ocho constantes AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY y sus SALT en wp-config.php. Eso invalida de golpe todas las sesiones abiertas, incluida la del atacante que tuviera una cookie válida. Tendrás que volver a entrar tú también: es lo esperado.

Tareas programadas, cron del panel y archivos ocultos

Esta sección explica por qué una web «limpia» amanece infectada otra vez. Empieza por el cron interno de WordPress:

SELECT option_value FROM wp_options WHERE option_name = 'cron';

Es un texto largo y feo, pero se lee: contiene nombres de eventos. Si ves uno que no corresponde a ningún plugin instalado, busca ese nombre en los archivos de la web.

Mira también el cron del servidor, en cPanel > Trabajos cron. Una tarea que ejecuta wget o curl contra un dominio externo cada pocas horas no la has puesto tú.

Y revisa los .htaccess, todos, no solo el de la raíz: hay uno posible en cada carpeta. Buscas tres cosas:

  • Bloques RewriteCond que miran el HTTP_USER_AGENT o el HTTP_REFERER para tratar distinto a Google o a quien llega desde un buscador. Eso es la firma de una redirección selectiva.
  • Directivas php_value auto_prepend_file, que cargan un archivo antes que cualquier otra cosa.
  • Líneas AddType o AddHandler que hacen que se ejecuten como PHP archivos con otra extensión (.jpg, .ico, .txt).

Comprueba lo mismo en .user.ini y en php.ini si existen en tu carpeta. Y si tienes SSH, este listado te da una foto muy útil de qué se ha tocado últimamente:

find . -type f -name "*.php" -mtime -20 -printf "%TY-%Tm-%Td %pn" | sort

Con honestidad: la fecha de modificación se puede falsear, y un atacante con oficio lo hace. Que un archivo tenga fecha antigua no lo limpia de sospecha; que la tenga reciente sí lo pone el primero de la cola.

Verificar el resultado desde fuera y desde dentro

Desde dentro, lo que quieres es comparar: que cada archivo del núcleo coincida con el oficial y que no quede ninguno de más. Los plugins de seguridad ayudan en esta fase, pero detectan lo que ya está catalogado y poco más; lo tienes explicado en el artículo sobre si los plugins de seguridad limpian de verdad una web hackeada.

Desde fuera es donde se ve la verdad, porque muchas infecciones solo se activan para ciertas visitas:

Cuando eso esté limpio, y solo entonces, entra en Search Console, mira Seguridad y acciones manuales > Problemas de seguridad y solicita la revisión. Pedirla antes de tiempo es perder días. Y después vigila quince días: la reinfección, cuando pasa, casi siempre pasa pronto.

Cuándo parar y llamar a alguien

No hace falta que pagues a nadie si tienes un blog o una web corporativa sencilla, sabes más o menos cuándo empezó el problema, tienes una copia de seguridad anterior a esa fecha y el ataque era de los ruidosos (una portada cambiada, un spam evidente). Restaurar, actualizarlo todo, regenerar claves y cambiar contraseñas es una tarde de trabajo y sale bien.

Para y busca ayuda profesional si se da alguna de estas situaciones:

  • Es una tienda con pedidos y datos de clientes. Ahí ya no hablamos solo de limpiar: hay que determinar si hubo acceso a datos personales, y eso tiene plazos legales.
  • Ya has limpiado una vez y ha vuelto. Significa que la puerta trasera sigue puesta o que el vector de entrada sigue abierto.
  • En el mismo hosting tienes varias webs. Se reinfectan entre ellas y limpiarlas de una en una no sirve de nada.
  • Ves indicios de que el atacante no está solo dentro de WordPress sino dentro del servidor: claves nuevas en ~/.ssh/authorized_keys, usuarios del sistema que no creaste, procesos raros.
  • No tienes acceso a la base de datos ni a los archivos, solo al panel de WordPress. Con eso no se puede limpiar bien, y no pasa nada por reconocerlo.

El coste orientativo de una limpieza profesional en España está en el entorno de los 500 euros para una web estándar, y lo que compras no es solo la desinfección: es que alguien determine por dónde entraron y lo cierre.

Preguntas frecuentes

No tengo ninguna copia anterior al ataque: ¿puedo limpiarla igualmente?

Sí. Este procedimiento no depende de una copia antigua: núcleo, plugins y temas se sustituyen por descargas oficiales, y lo que no se puede sustituir, wp-content y la base de datos, se revisa a mano. Haz igualmente la copia del estado infectado; la guía de Google recomienda incluso hacer dos, etiquetarlas como infectadas y limpiar sobre una copia, no sobre el servidor. Lo que cambia es que no tienes un punto limpio al que volver, así que trabaja sin prisa.

¿Cuánto tiempo lleva limpiar un WordPress a mano?

En una web sencilla, con conocimientos medios de WordPress y acceso por SFTP y a phpMyAdmin, entre cuatro y ocho horas de trabajo concentrado, como detallamos en cuánto cuesta limpiar una web hackeada. Una tienda, varias webs en el mismo hosting o una infección antigua lo alargan mucho. Súmale la revisión de Google, que va de unos días a unas semanas según el problema, y los quince días de vigilancia.

¿Qué cambia si la web es una tienda WooCommerce?

El procedimiento es el mismo, pero no basta con limpiar. Hay que comprobar si el proceso de compra fue manipulado para copiar tarjetas, revisar la pasarela de pago y las claves de API y determinar si se accedió a datos de clientes, porque eso activa plazos legales: notificar a la AEPD, cuando hay riesgo para las personas, tiene un plazo de 72 horas. Por eso en una tienda recomendamos parar y pedir ayuda. Las señales, en sospecho que roban tarjetas en mi tienda.

Fuentes oficiales

Cuando termines, no te quedes con la sensación de «creo que ya está». Pasa PathScan por tu dominio y comprueba gratis que no ha quedado nada: analiza tu web desde fuera, como la ve un visitante o un buscador, sin registro y sin tocar tu servidor. Si algo sigue activo, mejor lo ves tú hoy que un cliente mañana.