En pocas palabras: La inyección SQL en WordPress ocurre cuando un plugin, tema o el core concatena datos del usuario en consultas MySQL sin validarlos. Se previene usando $wpdb->prepare(), actualizando todo —el core 7.0.2 cerró CVE-2026-60137 el 17 de julio de 2026— y sumando WAF, backups y permisos mínimos en la base.

La inyección SQL en WordPress ocurre cuando un visitante escribe comandos SQL en una búsqueda o en un parámetro de URL, y la base de datos los ejecuta como si fueran consultas legítimas. El caso más serio del año: WordPress 7.0.2, publicada el 17 de julio de 2026, cerró una SQLi del core con explotación activa.

Una inyección SQL (SQLi) es una vulnerabilidad web que permite enviar comandos arbitrarios a la base de datos MySQL de un sitio a través de entradas que el código no valida. En WordPress aparece cuando plugins, temas o el core concatenan datos del usuario dentro de consultas, en lugar de usar consultas parametrizadas con $wpdb->prepare(). Las consecuencias van del robo de usuarios y hashes de contraseñas hasta la ejecución remota de código en el servidor, según documentan WP Security Ninja y TrustiWP.

En 30 segundos

  • WordPress 7.0.2, del 17 de julio de 2026, corrigió CVE-2026-60137: una inyección SQL sin autenticación en WP_Query, encadenable con un bug de rutas REST hasta lograr RCE.
  • La defensa principal es $wpdb->prepare(): separa la estructura SQL de los datos, así el input entra como literal y no como comando.
  • Patchstack ya registró intentos de explotación contra la vulnerabilidad del core, y el equipo de WordPress respondió forzando la actualización de los sitios afectados.
  • Cambiar el prefijo wp_ no zafa: es «seguridad» por oscuridad, y el atacante lista los nombres reales de las tablas desde information_schema.
  • El usuario de MySQL nunca debe ser root: mínimo privilegio para reducir el daño si el sitio cae en un hosting compartido.

¿Cómo se ve una consulta vulnerable a la inyección SQL en WordPress?

Una consulta vulnerable se reconoce rápido: un dato que viene del visitante, pegado dentro del string SQL sin ningún filtro. Ponele que armaste un buscador custom para tu tema y escribiste esto:

$busqueda = $_GET['s'];
$resultados = $wpdb->get_results(
 "SELECT * FROM {$wpdb->posts}
 WHERE post_title LIKE '%$busqueda%'"
);

El valor de $_GET[‘s’] viaja desde la URL tal como lo escribió el visitante, y MySQL lo ejecuta como sintaxis. Si alguien manda ?s=' UNION SELECT user_pass, 2, 3 FROM wp_users--, cerró la comilla que abriste vos, pegó su propia consulta y comentó el resto de la query con los guiones finales. Resultado: la página de resultados te muestra los hashes de contraseñas de tus usuarios.

Este patrón, input concatenado directo en el WHERE o el LIKE, es el clásico que explica TrustiWP, y aparece sobre todo en plugins y código custom. El core hace años que empuja a las APIs de alto nivel por ese motivo. (Spoiler: en julio de 2026 el core también apareció en la lista, llegamos a eso.)

¿Qué hace $wpdb->prepare() y cuándo hay que usarlo?

$wpdb->prepare() arma consultas parametrizadas: la estructura SQL va con placeholders (%s para texto, %d para enteros) y los datos viajan por separado, así que la base los trata como literales y no como comandos. Es la defensa principal contra la inyección SQL en WordPress, y la regla es una sola: si tu consulta toca un dato que viene del usuario, va con prepare(). Sin excepciones. Cubrimos ese tema en detalle en cómo configurar Sucuri en tu WordPress.

Para cláusulas LIKE hay un paso extra, porque los wildcards % y _ son sintaxis de LIKE: primero escapás el término con $wpdb->esc_like() y después le agregás vos los % de búsqueda:

$termino = '%' . $wpdb->esc_like( $_GET['s'] ) . '%';
$resultados = $wpdb->get_results(
 $wpdb->prepare(
 "SELECT ID, post_title FROM {$wpdb->posts}
 WHERE post_title LIKE %s AND post_status = 'publish'",
 $termino
 )
);

Con esto, lo que escriba el visitante queda encerrado como texto. Puede intentar cerrar comillas o pegar un UNION SELECT, pero para MySQL eso es un término de búsqueda, no una orden. La diferencia entre los dos bloques de código de este artículo es la diferencia entre un sitio seguro y una base entregada.

¿Dónde falla $wpdb->prepare()? Cuatro patrones que siguen siendo vulnerables

prepare() no es un talismán: protege los datos, no la estructura de la consulta. Estos cuatro patrones siguen generando vulnerabilidades reales:

  • ORDER BY armado con $_GET: los placeholders no sirven para nombres de columnas ni para ASC/DESC, así que mucha gente concatena directo. La solución es una allowlist de columnas hardcodeadas en PHP, y validar contra esa lista antes de armar la query.
  • Consultas anidadas con un segmento sin preparar: armás la query por partes y una queda concatenada porque «es interna». Si ese segmento toca input del usuario, el prepare del resto no te salva nada.
  • LIKE sin esc_like(): sin escapar los wildcards, un visitante manda %%%% y manipula la lógica del patrón de búsqueda, o te fuerza recorridos completos de tabla.
  • Inyección de segunda orden: sanitizar al escribir no protege al leer. Si un dato malicioso ya está en la base (en un campo de perfil, por ejemplo) y después lo leés y lo concatenás sin prepare, la inyección se ejecuta igual.

¿Qué datos de tu sitio quedan expuestos en un ataque de inyección SQL?

Una SQLi exitosa entrega la base completa. Tabla por tabla, esto es lo que queda a merced del atacante:

  • wp_users: usernames y hashes de contraseñas. WordPress usa bcrypt, lento de romper por diseño, pero los usuarios viejos con hashes MD5 legacy se crackean a miles de millones de intentos por segundo con hardware común.
  • wp_options: API keys, tokens y licencias de plugins que configuraste desde el admin. Con esas credenciales, el atacante sigue el camino hacia tus servicios externos.
  • wp_postmeta de WooCommerce: órdenes de compra y direcciones de facturación de tus clientes, con todo el problema legal que eso arrastra.
  • wp_usermeta: roles y capabilities de cada usuario, justo lo que sirve para planificar la escalada de privilegios.

Y el escalón peor: si el usuario de MySQL tiene el privilegio FILE (algo frecuente en shared hosting), el atacante puede usar SELECT … INTO OUTFILE para escribir archivos PHP en el servidor. Ahí la inyección SQL se convierte en ejecución remota de código, y de robo de datos pasás a sitio comprometido de punta a punta.

La vulnerabilidad de WordPress 7.0.2: SQLi sin autenticación por REST API

El 17 de julio de 2026, el equipo de WordPress lanzó la versión 7.0.2 para corregir CVE-2026-60137, una inyección SQL sin autenticación en WP_Query. El detalle técnico: el código solo sanitizaba author__not_in con array_map(‘absint’) cuando el parámetro llegaba como array; si llegaba como string, saltaba toda la validación y el valor se concatenaba directo al WHERE.

Y la falla no venía sola. Entre las versiones 6.9 y 7.0.1 se podía encadenar con una confusión de rutas en el batch endpoint de la REST API que bypasea la validación de entrada, y la cadena completa cuenta como ejecución remota de código (RCE). Un atacante sin cuenta en tu sitio (sí, en serio) podía llegar a ejecutar código en tu servidor.

Adam Kues, investigador de Assetnote/Searchlight Cyber, reportó el problema. Según el análisis de Patchstack, ya registraron intentos de explotación en la naturaleza, y el core respondió forzando la actualización de los sitios vulnerables. ¿Te conviene confiar en que esa actualización forzada llegó a tu instalación? Mejor no: verificá la versión vos mismo, que lo vemos ahora.

¿Qué versiones de WordPress corrigen la vulnerabilidad y qué hacer hoy?

Tres versiones, publicadas el mismo 17 de julio de 2026, dejan el problema cerrado:

VersiónQué incluyeDetalle
7.0.2Ambos parchesCorrige la SQLi de WP_Query y la confusión de rutas del batch REST
6.9.5Backport de ambosMismas correcciones para la rama 6.9
6.8.6Parche de SQLiLa rama 6.8 no tenía el bug de rutas; trae el fix de SQLi backporteado
inyección sql wordpress diagrama explicativo

La instrucción práctica es una línea: fijate qué versión corre tu sitio (la ves en el panel de admin) y si estás por debajo de 7.0.2, 6.9.5 o 6.8.6 según tu rama, actualizá hoy. No te apoyes solo en la actualización forzada: los sitios con actualizaciones bloqueadas, stagings congelados o instalaciones custom se quedaron atrás, y son los primeros que aparecen en los logs de los atacantes.

¿Cómo saber si tu WordPress fue hackeado con una inyección SQL?

No hay un cartel que lo avise, pero hay señales concretas que podés revisar hoy:

  • Usuarios admin que no creaste: ordená wp_users por fecha de registro; un admin desconocido es la señal más clara de escalada.
  • Spam en contenido y opciones: links raros en entradas viejas o redirecciones guardadas en wp_options que nadie cargó.
  • Patrones de ataque en los logs: si tenés logs de MySQL o de un WAF, buscá UNION, information_schema o comentarios — en las queries entrantes.

El patrón típico es siempre el mismo: entra por una consulta inyectable en un plugin que nadie actualiza, el atacante prueba un UNION SELECT, la base responde, extrae los hashes, crea un usuario admin, planta un backdoor y vos te enterás semanas después, cuando Google ya indexó links a casinos en tu blog. Si confirmás compromiso, el camino es restaurar desde un backup previo a la infección, rotar todas las contraseñas y claves de API, y reescanear todo con WPScan, que lista los plugins con vulnerabilidades conocidas.

¿Cambiar el prefijo de tablas o el usuario de la base protege contra la inyección SQL?

No, cambiar el prefijo wp_ no te protege de la inyección SQL. La idea suena linda: si las tablas no se llaman wp_users, el atacante no puede apuntarles. El problema es que apenas existe una consulta inyectable, el atacante lista los nombres reales de las tablas desde information_schema en segundos. Es seguridad por oscuridad, y la oscuridad dura una consulta.

Reconstruir un sitio en producción solo por el prefijo, con el riesgo de romper plugins que asumen wp_, no me parece una buena inversión. Lo que sí importa es el principio de mínimo privilegio: el usuario de MySQL que declarás en wp-config.php debe acceder solo a la base de ese sitio, con permisos justos. Nunca root, nunca un usuario compartido entre varios sitios. Si un sitio cae, que el daño quede encerrado ahí y no se contagie al resto del servidor. Te puede servir nuestra cobertura de blindar tu sitio contra los hackers.

Buenas prácticas para blindar tu WordPress contra la inyección SQL

No existe un botón mágico que blindé el sitio de un clic, pero esta checklist cubre lo que falla en la mayoría de los casos reales:

  • Instalá menos plugins y actualizalos rápido: la mayoría de los avisos de SQLi en WordPress son bugs de plugins, no del core. Cada plugin es superficie de ataque; si no lo usás, borralo.
  • Validá temprano, escapá tarde: validá el input apenas entra (tipo y largo) y aplicá prepare() o esc_html() recién en el punto de uso, según el contexto de salida.
  • Preferí las APIs de alto nivel: WP_Query, get_posts() y WP_User_Query manejan la sanitización por vos. Bajá a $wpdb crudo solo cuando no quede alternativa.
  • Allowlists para todo lo dinámico: columnas de ORDER BY y cualquier valor que termine formando parte de la estructura SQL se valida contra una lista hardcodeada en PHP.
  • wp_parse_id_list() para listas de IDs: limpia y convierte arrays de enteros antes de armar cláusulas IN, y te evita reinventar la sanitización (y equivocarte en el intento).
  • Backups automáticos y probados: si la base se compromete, restaurar limpio es el plan B. Un backup que nunca restauraste es una esperanza, no un backup.

Un WAF suma una capa que bloquea patrones de SQLi antes de que lleguen a PHP: Wordfence y Sucuri son las opciones de plugin más conocidas, y muchos hostings traen WAF a nivel de servidor. Y si querés ir más profundo, una auditoría de seguridad de WordPress completa revisa plugins, usuarios, permisos de archivos y configuración del servidor de una sola vez.

Errores comunes con la inyección SQL en WordPress

Después de años viendo sitios caídos, estos son los errores que se repiten:

  • Confiar en que «el plugin es premium, será seguro»: pagar licencia no implica código auditado. Los avisos de vulnerabilidades incluyen plugins premium todos los meses.
  • Escapar para pantalla y creer que eso sanitiza la query: esc_html() protege contra XSS en el navegador, pero no hace nada contra una SQLi en la consulta. Son contextos distintos, piden funciones distintas.
  • Instalar plugins nulled: los themes y plugins piratas vienen con código modificado y, en el mejor caso, versiones viejas con vulnerabilidades sin parchear. En el peor, traen backdoor incluido.
  • Pensar que actualizar lo cubre todo: actualizar cierra vulnerabilidades conocidas del core y de los plugins, pero el código custom de tu tema y tus queries manuales con $wpdb no se actualizan solos.

Preguntas Frecuentes

¿Qué es la inyección SQL en WordPress?

Es un ataque donde el atacante inserta comandos SQL a través de entradas del sitio (por ejemplo, un buscador o un parámetro de URL) que el código concatena sin validar en las consultas a la base. La base ejecuta esas órdenes como si fueran legítimas: puede leer usuarios y hashes, robar datos de clientes y, si MySQL tiene privilegio FILE, escribir archivos en el servidor.

¿Es seguro WordPress 7.0.2?

Sí. La 7.0.2, publicada el 17 de julio de 2026, corrige CVE-2026-60137 y la confusión de rutas del batch endpoint REST. Si tu sitio corre 7.0.2, 6.9.5 o 6.8.6, estás en una versión parchada. Ojo: la seguridad total depende también de plugins y temas al día, porque la mayoría de las SQLi reportadas salen de plugins.

¿Wordfence o Sucuri: cuál es mejor para evitar la inyección SQL?

Para SQLi puntualmente, ninguno reemplaza el código correcto: ambos son WAF que bloquean patrones de ataque conocidos antes de que lleguen a tu PHP. Wordfence corre como plugin en tu hosting y tiene versión gratuita; Sucuri combina WAF en la nube con servicio de limpieza. Elegí uno, no los dos juntos, y mantené el código con prepare() al día.

¿Actualizar WordPress y los plugins basta para evitar la inyección SQL?

No alcanza. Actualizar cubre las vulnerabilidades conocidas del core y de los plugins, que es la mayor parte del problema, pero no toca el código custom de tu tema ni tus consultas manuales con $wpdb. Combiná actualizaciones al día con revisión del código propio, menos plugins instalados y backups automáticos.

¿Los plugins nulled provocan inyección SQL en WordPress?

Los plugins y themes piratas son una vía directa de compromiso: suelen traer código modificado, backdoors o versiones viejas con vulnerabilidades conocidas sin parchear. Más allá de la SQLi, un nulled puede crear usuarios admin ocultos o comunicarse con servidores de quien lo modificó. Si el presupuesto aprieta, el repositorio oficial tiene alternativas gratuitas.

Conclusión

Lo que dejó 2026 hasta acá: la inyección SQL dejó de ser un problema exclusivo de plugins. CVE-2026-60137 demostró que hasta el core puede tener una SQLi sin autenticación, encadenable a RCE y con explotación activa que Patchstack ya registró. Si tu sitio corre algo anterior a 7.0.2, 6.9.5 o 6.8.6, actualizar es la tarea número uno, y es para hoy.

Para el día a día, la fórmula no cambió: prepare() en cada consulta que toque input de usuario, esc_like() en los LIKE, allowlists para columnas dinámicas, menos plugins y mínimo privilegio en MySQL. Ninguna de esas medidas tiene glamour; todas funcionan. Y si preferís que los backups y el hardening del servidor no dependan de tu memoria, un hosting WordPress con entorno endurecido cubre la parte que el código no puede: para sitios en Argentina, donweb.com es una opción sólida con esa orientación.

Fuentes