En pocas palabras: Hardening MySQL significa reducir la superficie de ataque del servidor: privilegios mínimos para el usuario de WordPress, acceso remoto al puerto 3306 cerrado, motor InnoDB y consultas blindadas contra inyección SQL. Según Wordfence, el 40% de los ataques buscan credenciales débiles.
El hardening de MySQL para WordPress se resume en cuatro movimientos: limitar los privilegios del usuario de la base de datos, cerrar el acceso remoto al puerto 3306, usar InnoDB como motor y blindar las consultas contra la inyección SQL. Cuatro frentes de configuración que reducen muchísimo el daño potencial de un ataque.
Para que quede claro desde el arranque: el hardening de MySQL es el proceso de reducir la superficie de ataque del servidor de bases de datos que sostiene un sitio WordPress. Cubre privilegios mínimos para el usuario de la aplicación, parámetros seguros en el archivo my.cnf, elección del motor de almacenamiento y defensas contra la inyección SQL. Lo aplica quien administra el servidor (o quien contrata un hosting que ya lo trae resuelto), y complementa los backups y la seguridad de plugins, no los reemplaza.
En 30 segundos
- Según el informe anual de Wordfence 2025, el 40% de los ataques a WordPress fueron fuerza bruta contra credenciales y el 56% explotó plugins vulnerables: en ambos casos, el botín final está en la base de datos.
- El usuario de WordPress necesita solo 8 privilegios (SELECT, INSERT, UPDATE, DELETE, ALTER, CREATE, INDEX, DROP). FILE, PROCESS, SUPER, RELOAD, SHUTDOWN y GRANT OPTION van revocados.
- Con
bind-address = 127.0.0.1ylocal-infile = 0en my.cnf cerrás la exposición externa del puerto 3306. - InnoDB es el motor por defecto desde MySQL 5.5 (2010): transacciones ACID y recuperación ante fallos que MyISAM nunca tuvo.
- Backups diarios con retención de 7 días y semanales con 4 semanas, guardados fuera del servidor.
¿Por qué la base de datos es el blanco principal de un ataque a WordPress?
Porque ahí vive todo: los usuarios con sus contraseñas hasheadas, los emails, el contenido, y la tabla wp_options con la configuración serializada del sitio. Un atacante con acceso a la base puede crear un usuario admin, inyectar scripts en cada artículo y redirigir tu tráfico a donde se le antoje. Tres consultas. Sitio comprometido.
Los números acompañan esta lectura. Según los datos del informe anual de Wordfence 2025, el 40% de los ataques registrados contra WordPress fueron intentos de fuerza bruta buscando credenciales válidas, mientras que el 56% apuntó a vulnerabilidades en plugins. Son las últimas cifras que publicó Wordfence antes del cierre de 2026 (cuando salga el corte nuevo conviene revisarlas), pero la tendencia viene sostenida desde hace años y coincide con lo que cualquiera ve en los logs de sus propios servidores. ¿Alguien verificó esos porcentajes de forma independiente? No que yo sepa, pero encajan con la experiencia de cualquiera que administre más de un sitio.
Ojo con una confusión típica: el hardening y los backups no son lo mismo. El backup restaura tu sitio después del desastre; el hardening busca que el desastre sea chico o directamente no ocurra. Necesitás las dos cosas, pero una no suple a la otra.
¿Qué es exactamente el hardening de MySQL en WordPress?
Es el conjunto de ajustes que reducen lo que un intruso puede hacer si llega a la base de datos. Opera en tres niveles: el del servidor (parámetros de my.cnf como bind-address o local-infile), el de la cuenta (qué privilegios tiene el usuario que usa wp-config.php) y el de la aplicación (consultas preparadas que impiden la inyección SQL). Si alguna vez abriste phpMyAdmin y viste que tu usuario tenía todos los privilegios marcados, ya sabés por dónde empezar. Para más detalles técnicos, mirá comparativa de suites de seguridad completas.
¿Qué permisos debe tener el usuario MySQL de WordPress?
Los justos para operar y actualizar el sitio, y ninguno más. Como detallan en WPSysadmin, el principio de privilegio mínimo aplica igual que en cualquier otro sistema: cada privilegio extra es una herramienta que le regalás al atacante si compromete la sesión.
- Operación diaria: SELECT, INSERT, UPDATE y DELETE cubren todo lo que WordPress hace mientras navegan tus visitantes.
- Mantenimiento: CREATE, ALTER, INDEX y DROP se usan cuando actualizás el core, activás un plugin o cambiás la estructura de las tablas.
- A revocar siempre: FILE (permite leer archivos del sistema operativo), PROCESS, SUPER, RELOAD, SHUTDOWN y GRANT OPTION, que habilitan escalada de privilegios o manipulación del propio servidor MySQL.
El comando queda así:
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'clave_larga_y_unica';
GRANT SELECT, INSERT, UPDATE, DELETE, ALTER, CREATE, INDEX, DROP
ON nombre_base.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
Fijate en el @'localhost': esa parte limita desde qué host puede conectarse la cuenta. Si dejás %, el usuario acepta conexiones desde cualquier IP del mundo, que es justo lo que no querés. Para desarrollo podés ser más laxo; en producción, la cuenta debe ser estricta:
| Privilegio | Desarrollo | Producción |
|---|---|---|
| SELECT, INSERT, UPDATE, DELETE | Sí | Sí |
| CREATE, ALTER, INDEX | Sí | Sí (las actualizaciones lo exigen) |
| DROP | Sí | Sí, con monitoreo |
| GRANT OPTION | Sí | No |
| FILE | Sí | No |
| PROCESS, SUPER, RELOAD, SHUTDOWN | No | No |

Una advertencia que circula en varias guías y merece matiz: algunos recomiendan revocar DROP «por las dudas». El problema es que las migraciones de esquema que corren las actualizaciones de core y plugins usan ALTER y DROP internamente, así que si lo revocás de forma permanente, tu próxima actualización de WooCommerce va a fallar a mitad de camino. Si querés jugar con esa idea, hacelo en ventanas de mantenimiento y restaurá el privilegio después.
¿Cómo cambiar el prefijo de las tablas de WordPress sin romper nada?
Ponele que heredás un sitio instalado hace diez años, con prefijo wp_ y treinta plugins acumulados. Cambiar el prefijo es viable, pero el diablo está en los detalles que nadie te cuenta hasta que el panel de usuarios deja de funcionar.
Método 1: instalación nueva
Editá wp-config.php antes de correr el instalador y cambiá la variable:
$table_prefix = 'wpx7k_';
Listo. Todas las tablas se crean con ese prefijo y no hay migración posible que romper.
Método 2: sitio existente, con SQL
Primero, backup completo con mysqldump (más abajo te muestro cómo). Después, renombrás las tablas y actualizás dos lugares donde WordPress guarda el prefijo «en duro»:
RENAME TABLE wp_posts TO wpx7k_posts;
RENAME TABLE wp_comments TO wpx7k_comments;
RENAME TABLE wp_options TO wpx7k_options;
RENAME TABLE wp_usermeta TO wpx7k_usermeta;
RENAME TABLE wp_users TO wpx7k_users;
UPDATE wpx7k_options SET option_name = 'wpx7k_user_roles'
WHERE option_name = 'wp_user_roles';
UPDATE wpx7k_usermeta SET meta_key = REPLACE(meta_key, 'wp_', 'wpx7k_');
Cambiás el prefijo en wp-config, renombrás las setenta tablas una por una, te olvidás de la fila wp_user_roles dentro de wp_options, el sitio levanta aparentemente bien, pero el acceso a usuarios te tira un error porque wp_capabilities sigue llamándose como antes, y pasás dos horas debuggeando algo que un backup previo habría resuelto en diez minutos. Ese guion lo vivió más de uno. Desactivá cachés y plugins de optimización durante la migración, y si es multisite, contá con tablas adicionales por sitio. Te puede servir nuestra cobertura de herramientas de limpieza tras un hackeo.
Método 3: con plugin
Brozzme DB Prefix & Change DB Prefix automatiza el proceso, incluida la actualización de wp_options y wp_usermeta. Igual hacés el backup antes: los plugins también fallan, y en plena migración de nombres de tabla no es el momento de descubrirlo.
Ahora, el mito. Hay quien presenta el cambio de prefijo como el «milagro» anti-hackers, y el análisis de AyudaWP es claro: zafa para frenar bots que escanean tablas wp_ a ciegas, pero contra una inyección SQL real no protege nada, porque quien logra ejecutar consultas lee el prefijo desde information_schema en segundos. Tratalo como higiene extra, nunca como defensa principal.
¿Cómo prevenir la inyección SQL en WordPress?
Validando toda entrada, usando consultas preparadas y limitando los privilegios del usuario para acotar el daño. La inyección SQL es la técnica donde el atacante mete código SQL dentro de un campo de formulario o parámetro de URL, y tu aplicación lo ejecuta creyendo que era dato común. Las variantes principales son cuatro:
- Error-based: provoca errores visibles que revelan estructura de la base.
- Union-based: usa UNION SELECT para volcar datos de otras tablas en la respuesta.
- Boolean-based: deduce información comparando respuestas verdadero/falso.
- Time-based: usa SLEEP() para confirmar inyecciones a ciegas midiendo tiempos de respuesta.
Ejemplo clásico de login: en el campo usuario escribís ' OR 1=1;-- y, si la consulta se arma concatenando strings, la condición se vuelve siempre verdadera y entrás sin contraseña. En código custom, el problema suele verse así:
// Vulnerable: concatena la entrada directa
$sql = "SELECT id FROM wp_users WHERE user_login = '" . $_POST['user'] . "'";
// Seguro: consulta preparada con parámetro vinculado
$sql = $wpdb->prepare(
"SELECT id FROM {$wpdb->users} WHERE user_login = %s",
$_POST['user']
);
$usuario = $wpdb->get_var($sql);
Las APIs nativas de WordPress ya trabajan con consultas preparadas, así que el riesgo concentrado está en tu código propio y en plugins mal hechos. Complementá con validación de entrada (sanitize_text_field, absint para enteros), escapado al mostrar datos, y una capa de WAF como Wordfence o Solid Security que filtre patrones de ataque antes de que lleguen a PHP. Y acá el hardening de permisos vuelve a sumar: si tu usuario de base no tiene FILE ni acceso a otras bases, una inyección exitosa encuentra mucho menos para robar.
¿Acceso local o remoto: cómo configurar my.cnf para endurecer MySQL?
Local siempre que puedas. Si WordPress y MySQL comparten servidor, la conexión por socket localhost elimina la exposición del puerto 3306 a internet y baja la latencia de cada consulta. El bloque de configuración mínimo en my.cnf (Linux) o my.ini (Windows) queda así:
[mysqld]
bind-address = 127.0.0.1
local-infile = 0
skip-name-resolve
# Corta TCP por completo (solo apps en el mismo servidor):
# skip-networking
- bind-address = 127.0.0.1: MySQL solo escucha en la interfaz local; desde afuera, el puerto 3306 ni existe.
- local-infile = 0: desactiva LOAD DATA LOCAL INFILE, que permite leer archivos del cliente si la app fue comprometida.
- skip-networking: versión extrema, desactiva TCP y deja solo sockets; ideal si nada necesita conectarse por red.
- Verificación: corré
ss -tlnp | grep 3306y comprobá que la escucha esté en 127.0.0.1, no en 0.0.0.0.
¿Y si necesitás acceso remoto sí o sí, por ejemplo con la base en un servidor aparte? Entonces creá el usuario amarrado a una IP específica ('wp_user'@'203.0.113.10', jamás %), filtrá el puerto 3306 en el firewall para esas IPs únicamente, y exigí cifrado con require_secure_transport = ON para que las credenciales no viajen en texto plano. Relacionado: parchear vulnerabilidades críticas a tiempo.
Eso sí, si estás en hosting compartido no vas a poder tocar my.cnf, y acá la jugada cambia: lo importante es elegir un proveedor que ya aplique estas restricciones por defecto. Antes de asumir que cualquier plan sirve, compará opciones locales como donweb.com y preguntale al soporte si la base queda en localhost y aislada por cuenta. Un minuto de pregunta ahorra un incidente entero.
¿InnoDB o MyISAM: cuál conviene para WordPress en 2026?
InnoDB, sin vueltas. Es el motor por defecto de MySQL desde la versión 5.5 (2010) y MyISAM quedó como tecnología legacy. La diferencia crítica es de seguridad operativa: MyISAM no tiene transacciones ni recuperación automática ante fallos, así que un corte de luz a mitad de escritura te puede dejar tablas corruptas. InnoDB registra cada cambio en un redo log y reconstruye el estado consistente al reiniciar.
| Característica | InnoDB | MyISAM |
|---|---|---|
| Transacciones ACID | Sí | No |
| Bloqueo | Por fila | Por tabla completa |
| Recuperación ante fallos | Automática (redo log) | Manual, con riesgo de corrupción |
| Llaves foráneas | Sí | No |
| Rendimiento concurrente | Alto | Pobre con escrituras simultáneas |
| Estado en 2026 | Estándar recomendado | Deprecado en la práctica |
Como explica AyudaWP en su comparativa, el bloqueo por fila de InnoDB además evita el clásico cuello de botella de MyISAM, donde una sola escritura bloquea la tabla entera y con ella, medio sitio. Para convertir una tabla:
ALTER TABLE wp_posts ENGINE=InnoDB;
Repetilo por cada tabla que siga en MyISAM (desde phpMyAdmin se puede hacer por lotes desde la pestaña Operaciones). Hacé backup primero igual: ALTER sobre tablas grandes toma su tiempo y consume espacio temporal.
¿Cómo aislar bases de datos y automatizar backups?
Una instalación de WordPress, una base de datos, un usuario exclusivo. Si alojás varios sitios en el mismo servidor, esta regla es la que determina el radio de la explosión: si comprometen la base del sitio A, el sitio B ni se entera porque comparten servidor, no credenciales. Mezclar todo en una sola base con un solo usuario es regalar el efecto dominó, como advierten también en WPDirecto.
Para los backups, un esquema probado: copia diaria con retención de 7 días, más una semanal que se conserva 4 semanas. Con mysqldump y cron queda armado en cinco minutos:
# Crontab: todos los días a las 3 AM
0 3 * * * mysqldump --single-transaction -u backup_user -p'ClaveSegura' \
nombre_base | gzip > /var/backups/mysql/nombre_base_$(date +\%F).sql.gz
El flag –single-transaction usa las transacciones de InnoDB para sacar la copia sin bloquear el sitio. Alternativas: wp db export si usás WP-CLI, o plugins como WPVivid o BackWPup si preferís manejarlo desde el panel de WordPress. Dos reglas de oro: el destino de los backups debe estar fuera del servidor (almacenamiento remoto o descarga programada), y jamás dentro de la carpeta pública, porque un backup olvidado en public_html es un volcado completo de tu base descargable por cualquiera que adivine el nombre del archivo.
¿Cómo auditar MySQL y detectar intentos de ataque?
Revisando logs, aplicando mysql_secure_installation y configurando Fail2Ban para que los intentos repetidos terminen en IP bloqueada. El log de errores debería estar siempre activo; en Debian y Ubuntu lo encontrás en /var/log/mysql/, y en distribuciones Red Hat suele estar en /var/lib/mysql/. El query logging general (general_log) muestra cada consulta que entra, pero come recursos, así que activalo por ratos cortos cuando investigues algo puntual, no como política permanente.
¿Qué herramientas gratuitas sirven para auditar MySQL?
Tres, todas incluidas o gratis:
- mysql_secure_installation: asistente interactivo que elimina usuarios anónimos, borra la base test, impide login remoto de root y fija contraseña de root. Si instalaste MySQL y nunca lo corriste, es tu primera tarea de hoy.
- MySQLTuner: script de Perl que analiza variables, memoria y estadísticas, y sugiere ajustes de configuración con prioridades claras.
- ss / netstat: para confirmar en qué interfaz escucha el 3306 y cerrar lo que sobra.
Frecuencia recomendada: repaso semanal de logs en producción. No tiene por qué tomar más de diez minutos si sabés qué buscar. Tema relacionado: detener ataques antes de llegar a la base.
¿Cómo saber si tu base de datos fue comprometida?
Hay señales que saltan rápido si mirás:
- Usuarios admin desconocidos: consultá wp_users ordenando por fecha de registro y verificá cada alta reciente.
- Contenido inyectado: links spam o scripts dentro de wp_posts, típicamente en campos que no editás seguido.
- Tareas cron extrañas: entradas sospechosas en la opción cron de wp_options que ejecutan URLs externas.
- Intentos de conexión fallidos masivos: ráfagas en el log de errores de MySQL indican fuerza bruta en curso.
Fail2Ban cierra el círculo: con el filtro mysqld-auth podés configurar que una IP que acumule 5 fallos de autenticación quede bloqueada durante una hora. Es configuración de una tarde y elimina el 99% del ruido de fuerza bruta contra el 3306 (si es que seguiste el consejo de no exponerlo, mejor todavía).
Errores comunes al hacer hardening de la base de datos de WordPress
- Darle ALL PRIVILEGES al usuario «para que no falle nada»: es el atajo más común y el peor. Cada privilegio extra (FILE, SUPER, acceso a otras bases) amplifica el daño de una eventual inyección. Usá la lista de ocho privilegios de este artículo.
- Usar root en wp-config.php: root tiene SUPER y FILE, o sea que una inyección SQL exitosa puede terminar leyendo archivos del sistema. Creá un usuario dedicado con su base asignada y borrá esa costumbre de raíz.
- Guardar backups en la carpeta pública: un archivo .sql.gz dentro de public_html queda a un guess de distancia de ser descargado. Mandá los backups fuera del webroot y con permisos restringidos.
- Cambiar el prefijo y relajarse: el prefijo nuevo filtra bots tontos, nada más. Si dejaste el usuario con ALL PRIVILEGES y el 3306 abierto al mundo, cambiaste el tapete pero dejaste la puerta abierta.
- Revocar DROP y ALTER «por seguridad»: rompés las actualizaciones de core y plugins a mitad de migración, que suele ser peor que el riesgo original. Si lo hacés, que sea temporal y documentado.
Preguntas Frecuentes
¿Qué permisos debe tener el usuario MySQL de WordPress?
Ocho: SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX y DROP sobre su única base de datos. Evitá FILE, PROCESS, SUPER, RELOAD, SHUTDOWN y GRANT OPTION, que permiten leer archivos del servidor, manipular procesos o escalar privilegios. Con ese set WordPress instala, actualiza y opera sin problemas.
¿Cambiar el prefijo de tablas protege contra ataques?
Filtra bots que buscan tablas wp_ a ciegas, pero no detiene una inyección SQL real: quien consigue ejecutar consultas lee el prefijo desde information_schema en segundos. Tratalo como una capa de higiene adicional, nunca como defensa principal.
¿Conviene conectar WordPress a MySQL en local o remoto?
Local siempre que puedas: la conexión por socket localhost elimina la exposición del puerto 3306 y reduce latencia. Acceso remoto solo si separás capas, y en ese caso con el usuario amarrado a una IP específica (nunca %), firewall restrictivo y TLS obligatorio vía require_secure_transport.
¿Es seguro usar phpMyAdmin o Adminer para administrar la base?
Sí, si están actualizados y protegidos: accedé detrás de VPN o con autenticación adicional, y cerralos cuando no los uses. phpMyAdmin ofrece más funciones; Adminer es un único archivo PHP, práctico para subir y borrar. El riesgo real es dejarlos públicos con credenciales débiles.
¿MariaDB funciona igual que MySQL para WordPress?
Sí. MariaDB nació como fork compatible de MySQL y WordPress corre en ambos sin cambios de código; muchos hosts lo traen por defecto. Todos los ajustes de este artículo (privilegios, my.cnf, consultas preparadas) aplican igual en los dos motores.
Conclusión
El hardening de MySQL para WordPress no requiere presupuesto: requiere una hora y una lista. En orden de impacto, empezá por el usuario de base de datos con privilegios mínimos (elimina el efecto dominó de cualquier inyección), seguís por cerrar el 3306 con bind-address y local-infile, verificás que todas tus tablas estén en InnoDB, revisás que tu código custom use prepare(), y cerrás con backups aislados fuera del servidor y Fail2Ban vigilando los accesos fallidos.
La razón para hacerlo ahora y no después es simple: los ataques a WordPress están automatizados y los escáneres prueban millones de sitios por día esperando que alguno tenga la puerta entreabierta. Una guía de hardening de 2026 como la de MeetCyber lo confirma: la base sigue siendo el objetivo final de casi cualquier cadena de ataque. Tu sitio probablemente cumpla tres o cuatro de los puntos de esta lista. Fijate cuáles faltan, y cerralos esta semana.
Fuentes
- Codex WordPress (oficial) – Fortaleciendo WordPress: guía oficial de endurecimiento en español
- WPSysadmin – Permisos recomendados para la base de datos de WordPress
- AyudaWP – Cómo cambiar el prefijo de la base de datos de WordPress (y qué protege)
- WPDirecto – Seguridad en bases de datos para WordPress
- MeetCyber (Medium) – WordPress Security in 2026: The Complete Hardening Guide