En resumen: para revisar la base de datos en busca de código malicioso, recorra una exportación o una copia con consultas de solo lectura para localizar scripts inyectados, direcciones ajenas y código ofuscado en contenidos y ajustes. Revise además los triggers, los eventos programados, los procedimientos almacenados y los usuarios de la base de datos. Limpie solo tras la valoración y nunca con un buscar y reemplazar a ciegas, porque los datos serializados se romperían.
El punto ciego de muchas limpiezas
Tras una intrusión, la mayoría de los responsables revisan los archivos. Sustituyen el núcleo del CMS, borran los archivos PHP sospechosos y reinstalan las extensiones. Después el sitio web parece limpio y una semana más tarde vuelve a redirigir a páginas ajenas. La razón suele estar en la base de datos, que se mantuvo sin cambios durante la limpieza.
Para los atacantes, la base de datos es un escondite atractivo. Un script en un widget se sirve en todas las páginas. Un ajuste de dirección modificado desvía a todos los visitantes. Una cuenta de administrador adicional abre la puerta para más adelante. Nada de esto llama la atención en un escaneo de archivos.
Tras PHP2Shell, las vulnerabilidades CVE-2026-109618 y CVE-2026-109619 con ejecución de código sin autenticación, la base de datos forma por eso parte de toda revisión. Quien puede ejecutar código en el servidor suele poder leer las credenciales de la base de datos desde la configuración y escribir en ella a voluntad. Cómo habla la aplicación con la base de datos lo explica PHP y bases de datos: PDO y mysqli.
Dónde se esconde el código malicioso en la base de datos
| Lugar del hallazgo | Contenido típico | Efecto |
|---|---|---|
| Contenidos (entradas, páginas, productos) | bloques <script>, enlaces invisibles, <iframe> |
código malicioso o spam para los visitantes |
| Ajustes y opciones | dirección del sitio modificada, scripts ajenos en widgets u opciones del tema | redirecciones, scripts en todas las páginas |
| Tablas de usuarios | administradores adicionales | acceso permanente |
| Triggers | código que modifica contenidos en cada guardado | el código malicioso reaparece tras la limpieza |
| Eventos programados | tareas recurrentes en el servidor de base de datos | cambios sin acceso web |
| Usuarios de la base de datos | cuentas adicionales con amplios derechos | acceso sin pasar por el CMS |
Las tres últimas filas se pasan por alto con especial frecuencia porque no están en las tablas del CMS, sino en el propio servidor de base de datos.
En las tiendas online, un lugar merece especial atención: los ajustes con los que se incluyen scripts adicionales en la caja y en la finalización del pedido. Un script ajeno en este punto puede leer los datos que introducen los clientes antes de que se envíen cifrados al servicio de pago. Por eso revise de forma específica todos los campos para código de cabecera y pie de página, las inclusiones de seguimiento y los ajustes de diseño que actúan en las páginas de la caja.
Piense además en otras bases de datos en el mismo servidor. Las copias de prueba y de staging, versiones antiguas de proyectos o bases de datos de otros sitios web suelen estar al lado y son accesibles con las mismas credenciales. Lo que se encuentre en la base de datos en producción debería revisarse también allí antes de que una copia vuelva a servir de base más adelante.
Revisar la base de datos en busca de código malicioso: contenidos y ajustes
Trabaje sobre una exportación. Es a la vez conservación de pruebas y base de búsqueda. La exportación puede recorrerse como archivo de texto sin cargar la base de datos en funcionamiento:
# Crear una exportación consistente (se solicitan las credenciales)
mysqldump --single-transaction --routines --triggers --events -u BENUTZER -p DATENBANK > export.sql
sha256sum export.sql > export.sql.sha256
# Contar patrones típicos en la exportación
grep -oiE '<script[^>]{0,80}' export.sql | sort | uniq -c | sort -rn | head -30
grep -oiE '(eval\(|base64_decode|fromCharCode|document\.write|atob\()' export.sql | sort | uniq -c
grep -oiE 'https?://[a-z0-9.-]+' export.sql | sort | uniq -c | sort -rn | head -50
La última consulta lista, por frecuencia, todos los dominios que aparecen en la base de datos. Los dominios ajenos que nadie del equipo conoce son los candidatos más importantes. Para consultas específicas en la propia base de datos sirven las instrucciones SELECT. El ejemplo usa tablas de WordPress; el prefijo y los nombres de tabla de otros sistemas figuran en su documentación:
-- Ajustes con scripts o funciones sospechosas
SELECT option_name, LENGTH(option_value) AS laenge
FROM wp_options
WHERE option_value LIKE '%<script%'
OR option_value LIKE '%eval(%'
OR option_value LIKE '%base64_decode%'
ORDER BY laenge DESC;
Las entradas con un contenido muy grande merecen especial atención. La guía Encontrar puertas traseras en WordPress ofrece información de fondo sobre los escondites específicos de WordPress.
Triggers, eventos y cuentas de la base de datos
En muchas aplicaciones web los triggers y los eventos programados ni siquiera están previstos. Si existen, alguien debería poder explicar por qué. Estas consultas solo leen de las tablas del sistema de MySQL y MariaDB:
-- Triggers en todas las bases de datos
SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_OBJECT_TABLE, CREATED
FROM information_schema.TRIGGERS;
-- Eventos programados
SELECT EVENT_SCHEMA, EVENT_NAME, STATUS, CREATED, LAST_EXECUTED
FROM information_schema.EVENTS;
-- Procedimientos y funciones almacenados
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, CREATED
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema');
-- Usuarios de la base de datos
SELECT user, host FROM mysql.user ORDER BY user;
Si su aplicación usa PostgreSQL u otro sistema de bases de datos, las tablas del sistema y los comandos tienen otros nombres; el principio es el mismo. Para el análisis conviene un usuario de base de datos propio que solo pueda leer. Así, ni siquiera un error de escritura en una consulta puede modificar nada, y la revisión no deja rastros en los datos.
Un trigger que añade código al guardar una entrada explica por qué el código malicioso vuelve tras cada limpieza. Los usuarios de base de datos desconocidos forman parte de la revisión de cuentas igual que las cuentas de administrador del CMS; más información en Encontrar cuentas de administrador ajenas en el CMS y el servidor.
Limpiar con cuidado sin destruir datos
La limpieza es la parte más delicada. Este orden ha dado buen resultado:
- Valorar los hallazgos: cada coincidencia se clasifica como conocida, explicable e inofensiva o ajena. Los códigos de seguimiento y de mapas suelen ser legítimos.
- Cerrar la vía de entrada: llevar PHP a una versión corregida (8.5.12, 8.4.27, 8.3.36, 8.2.35 o backport confirmado), reiniciar todos los workers y actualizar la aplicación.
- Eliminar de forma específica: limpie los contenidos ajenos desde la aplicación o con herramientas que traten correctamente los datos serializados. Borre los triggers, eventos y cuentas ajenos.
- Cambiar las credenciales: nueva contraseña de la base de datos, anotada en la configuración de la aplicación, y derechos del usuario de la aplicación limitados a su propia base de datos.
- Volver a comprobar: repetir las consultas de búsqueda al cabo de unos días.
No reemplace nada con buscar y reemplazar en la exportación SQL. Los datos PHP serializados guardan la longitud de cada cadena. Si la longitud cambia, el ajuste queda ilegible y el sitio web puede dejar de funcionar.
Las redirecciones que se producen a pesar de una base de datos limpia tienen su causa en otra capa. El artículo El sitio web redirige a páginas ajenas ayuda a acotarla.
¿Cómo ayuda PHP SOS en concreto?
- Aseguramos la base de datos con suma de verificación y recorremos la exportación en busca de scripts, dominios ajenos y código ofuscado.
- Revisamos triggers, eventos, procedimientos y usuarios de la base de datos que están fuera del CMS.
- Limpiamos los hallazgos en el marco de la eliminación de malware, sin dañar los datos serializados.
- Limitamos los derechos del usuario de la aplicación y repetimos la revisión tras la limpieza.
Preguntas frecuentes
¿Puede haber código malicioso en la base de datos?
Sí. Los atacantes escriben JavaScript en entradas, descripciones de productos o widgets, modifican ajustes de dirección para provocar redirecciones o crean cuentas de administrador. En MySQL y MariaDB, además, los triggers y los eventos programados pueden modificar datos. Un escaneo de archivos no encuentra nada de esto.
¿Cómo busco en la base de datos sin estropear nada?
Trabaje sobre una exportación o una copia y utilice solo consultas SELECT. Además, una exportación puede recorrerse con grep como archivo de texto. Los cambios se hacen solo cuando se ha valorado cada hallazgo y existe una copia de seguridad.
¿Por qué no debo limpiar los hallazgos con buscar y reemplazar en la exportación?
Muchos CMS guardan los ajustes como datos PHP serializados, en los que se almacena también la longitud de cada cadena. Un reemplazo cambia la longitud pero no el valor indicado, y el ajuste queda ilegible. Limpie desde la aplicación o con herramientas que comprendan los datos serializados.
¿Es sospechoso todo script de la base de datos?
No. Los códigos de seguimiento, los mapas, los vídeos o los widgets de chat también se guardan como script. Lo decisivo es que alguien del equipo conozca la entrada y la dirección de destino.