La Prevención XSS y SQL Injection exige proteger capas distintas: las consultas a base de datos deben usar sentencias preparadas, mientras que los datos mostrados en pantalla deben escaparse según el contexto.
Índice
Anuncio
XSS y SQL injection requieren controles distintos
XSS busca que el navegador de una persona ejecute contenido malicioso, mientras que SQL Injection intenta alterar una consulta contra MySQL o MariaDB. Por ello, se requieren consultas parametrizadas para SQL y escape contextual para XSS.
Vector, impacto y defensa principal
| Criterio | XSS | SQL Injection |
|---|---|---|
| Entrada habitual | Comentarios, perfiles, URL, campos de texto o JavaScript | Filtros, formularios, API, AJAX o consultas personalizadas |
| Capa afectada | Navegador de quien visita o administra | Base de datos y servidor |
| Impacto típico | Robo de sesión, cambio de contenido o acciones con la cuenta abierta | Lectura, cambio o borrado de registros |
| Control obligatorio | Escape de salida según HTML, atributo, URL o JavaScript | Consulta preparada con parámetros separados |
| Apoyo útil | CSP, cookies HttpOnly y límites de HTML permitido | WAF, permisos mínimos y revisión de logs |
Una entrada puede necesitar dos defensas
Cuando un valor termina en SQL y también se imprime en una página, ambos controles deben aplicarse de forma independiente.
Cada dato exige validar, limpiar y codificar
Validar comprueba si un dato cumple una regla permitida, sanitizar lo normaliza o elimina partes no deseadas, parametrizar lo separa del código SQL y escapar lo convierte en texto seguro para un lugar concreto.
Validar antes de aceptar el dato
Los nombres de tablas, columnas y fragmentos como ORDER BY no se parametrizan como un valor normal: deben salir de una lista cerrada controlada por el programa, porque son parte de la estructura de la consulta.
Escapar depende del lugar de salida
esc_html() protege texto dentro de HTML, esc_attr() protege atributos, esc_url() trata direcciones web y wp_json_encode() prepara datos PHP para JavaScript.
Php // Inseguro: el dato modifica el código SQL. $sql = "SELECT * FROM {$wpdb->posts} WHERE ID = " . $_GET['id']; $rows = $wpdb->get_results($sql);
// Seguro: el valor queda separado de la consulta. $id = absint($_GET['id'] ?? 0); $sql = $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", $id ); $rows = $wpdb->get_results($sql);
php // Inseguro si $nombre procede de una entrada externa. Echo '
// Seguro: cada salida usa su contexto. Echo '
Anuncio
WordPress seguro combina parcheado y APIs
WordPress reduce la exposición a vulnerabilidades cuando el núcleo, los plugins y los temas están al día, y el código propio usa sus APIs seguras.
Wpdb::prepare evita concatenar SQL
wpdb::prepare() inserta marcadores como %d para enteros, %s para texto y %f para decimales, manteniendo el valor separado de la estructura SQL.
Php $estado = sanitize_key($_GET['estado'] ?? 'draft'); $estados = array('draft', 'publish', 'private'); $estado = in_array($estado, $estados, true) ? $estado : 'draft';
$query = $wpdb->prepare( "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_status = %s", $estado );
Roles y permisos limitan el daño
El principio de mínimo privilegio significa que cada cuenta recibe solo los permisos necesarios para su tarea: un editor no necesita administrar plugins, y la cuenta de MySQL no debería tener privilegios globales.
El código PHP y JavaScript necesita doble revisión
Los desarrollos PHP y JavaScript a medida deben validar cada entrada, parametrizar cualquier consulta y codificar toda salida en el punto donde el navegador la interpreta.
innerHTML puede convertir texto en código
XSS basado en DOM aparece cuando JavaScript toma datos de una URL, API o localStorage y los inserta como HTML; si solo necesitas mostrar texto, textContent es la opción más segura.
Javascript // Inseguro: interpreta el valor como HTML. Resultado.innerHTML = new URLSearchParams(location.search).get('q');
// Seguro para mostrar texto. Resultado.textContent = new URLSearchParams(location.search).get('q') || '';
JSON válido no basta al renderizarlo
JSON es un formato de datos, no una garantía de seguridad al incrustarlo en HTML; aunque wp_json_encode() prepare datos PHP para JavaScript, el cliente debe usar textContent, nodos DOM o una plantilla que codifique valores.
CSP, cookies y WAF reducen el impacto
CSP, SRI, HTTPS, cookies seguras y un WAF son capas de contención, no sustitutos del código correcto.
Cookies seguras limitan sesiones expuestas
HttpOnly impide que JavaScript lea directamente una cookie, Secure obliga a enviarla solo por HTTPS y SameSite limita en qué peticiones entre sitios se adjunta.
El WAF filtra, pero no entiende todo
Un Web Application Firewall analiza peticiones y bloquea patrones conocidos antes de que lleguen a WordPress, pero puede fallar ante XSS basado en DOM, rutas internas o lógica de negocio defectuosa.
Un firewall para WordPress puede añadir una capa de filtrado frente a peticiones maliciosas conocidas. Debe acompañar al parcheado y a la corrección del código, nunca reemplazarlos.
- Puede bloquear patrones de ataque repetidos antes de que alcancen formularios y rutas públicas.
- Permite registrar peticiones sospechosas para investigar intentos contra WordPress.
- Añade una barrera cuando una actualización crítica necesita validación antes de aplicarse.
Una CSP eficaz debe enviarse como cabecera HTTP y partir de una política restrictiva, por ejemplo, limitando script-src a 'self' y a orígenes concretos que sean imprescindibles. Evita habilitar 'unsafe-inline' y 'unsafe-eval', porque reducen de forma importante la protección frente a ataques XSS; cuando el sitio necesita scripts inline, los nonces por respuesta o los hashes permiten autorizarlos de forma controlada. SRI (integrity) añade otra defensa al cargar scripts o hojas de estilo desde una CDN: el navegador verifica que el archivo recibido coincide con el hash declarado.
Estas capas no sustituyen el escape contextual ni la sanitización de entradas. Asimismo, HttpOnly evita la lectura directa de la cookie desde JavaScript, pero un XSS aún puede realizar acciones con la sesión activa; SameSite mitiga principalmente CSRF y Secure exige HTTPS.
Anuncio
Auditar antes del incidente evita decisiones a ciegas
Una auditoría útil une inventario de plugins, revisión de código, análisis SAST, pruebas DAST, logs, WAF y un plan de respuesta.
Qué debe comprobar una auditoría
- Inventario de núcleo, plugins, temas, versiones PHP y extensiones sin uso, con revisión de CVE y parcheado disponible.
- Pruebas de formularios, buscadores, REST API, AJAX, importadores CSV, checkout y paneles de administración.
- Revisión SAST de consultas concatenadas, salidas sin escape, `innerHTML` y controles de autorización ausentes.
- Revisión DAST de rutas públicas, cabeceras HTTP, sesiones, errores visibles y respuestas anómalas.
- Logs de servidor, PHP, WordPress, WAF y base de datos, con retención acorde al riesgo y a las obligaciones aplicables.
Responder en las primeras 72 horas
Ante una explotación confirmada, conserva logs, aísla el vector, revoca sesiones y credenciales afectadas, corrige el código y restaura solo copias verificadas si es necesario.
La mejor decisión preventiva es exigir pruebas de que cada dato se parametriza al llegar a SQL y se escapa al salir al navegador. CSP, nonces, cookies y WAF reducen el alcance de un fallo, pero no sustituyen esas dos comprobaciones.
Preguntas frecuentes
¿Qué es una inyección SQL y cómo evitarla?
Una inyección SQL ocurre cuando un dato manipulado cambia una consulta a MySQL o MariaDB. Se evita con consultas preparadas como wpdb::prepare() y permisos mínimos para la cuenta de base de datos; validar el dato no sustituye parametrizarlo.
¿Cómo evito ataques XSS en WordPress?
Evita XSS escapando cada salida con esc_html(), esc_attr() o esc_url() según el lugar donde se muestre. Si permites HTML, limita etiquetas con wp_kses() y revisa JavaScript que use innerHTML.
¿Un nonce de WordPress protege contra XSS?
No, un nonce protege sobre todo frente a CSRF y tiene una validez temporal limitada. Debe combinarse con current_user_can(), validación de entradas, escape de salida y consultas preparadas.
¿Cada cuánto debo revisar la seguridad web?
Revisa plugins y temas al menos cada 30 días y tras cada aviso crítico o cambio de código. Una auditoría puede hacerse entre cada 6 y 12 meses en sitios estables, pero una tienda o portal con datos personales necesita controles entre 1 y 3 meses.
Prioridad: corregir origen y comprobar capas
La prioridad práctica es localizar cualquier SQL concatenado y cualquier dato que llegue a HTML o JavaScript sin escape contextual.
- Pagar limpiezas sueltas no frena el malware recurrente
- Subir SVG sin revisar compromete la seguridad en WordPress
Josu Barrios
Somos especialistas en mantenimiento WordPress para empresas, profesionales y tiendas online. Contamos con experiencia en seguridad web, optimización de rendimiento, actualizaciones, copias de seguridad y resolución de incidencias técnicas, ayudando a que cada sitio funcione de forma rápida, estable y protegida. Nuestro enfoque combina soporte técnico profesional, buenas prácticas de seguridad y seguimiento continuo para ofrecer un servicio fiable, transparente y orientado a resultados reales.