Si realizas análisis de seguridad en tu sitio WordPress, es posible que te hayas encontrado con alertas sobre inyección en la query string relacionadas con la REST API, específicamente en URLs como /wp-json/wp/v2/posts/. En este artículo te explicaré qué significa esta vulnerabilidad, por qué es peligrosa y cómo solucionarla.
#¿Qué es la REST API de WordPress?
La REST API de WordPress es una interfaz que permite a aplicaciones externas comunicarse con tu sitio. Aunque es útil para desarrolladores, también puede exponer información sensible si no está correctamente configurada.
Por defecto, WordPress expone endpoints como:
/wp-json/wp/v2/posts– Lista todos los posts/wp-json/wp/v2/posts/123– Muestra un post específico/wp-json/wp/v2/users– Lista todos los usuarios/wp-json/wp/v2/pages– Lista todas las páginas
#¿Qué es la vulnerabilidad de inyección en Query String?
La inyección en query string ocurre cuando un atacante manipula los parámetros de la URL para:
- Inyección SQL: Ejecutar comandos maliciosos en la base de datos
- XSS (Cross-Site Scripting): Inyectar código JavaScript malicioso
- Information Disclosure: Acceder a información que debería estar protegida
Un ejemplo de ataque sería:
https://tu-sitio.com/wp-json/wp/v2/posts?search=' UNION SELECT * FROM wp_users--
#Peligros de esta vulnerabilidad
| Riesgo | Consecuencia |
|---|---|
| Robo de datos | Acceso a usuarios, contraseñas hasheadas, emails |
| Defacement | Modificación del contenido del sitio |
| Backdoors | Instalación de puertas traseras para acceso futuro |
| SEO Spam | Inyección de enlaces maliciosos que dañan tu posicionamiento |
| Ransomware | Cifrado de la base de datos exigiendo rescate |
#
#Cómo detectar si eres vulnerable
Puedes verificar manualmente si tu REST API está expuesta visitando estas URLs en tu navegador:
https://tu-sitio.com/wp-json/wp/v2/posts https://tu-sitio.com/wp-json/wp/v2/users https://tu-sitio.com/wp-json/wp/v2/pages
Si ves un listado JSON con información de tus posts, usuarios o páginas, tu REST API está expuesta.
También puedes usar herramientas de análisis como:
#Solución: Proteger la REST API
La solución consiste en añadir código a tu archivo functions.php del tema activo (o mejor aún, en un plugin de funcionalidades). Este código realiza tres acciones:
- Deshabilita endpoints innecesarios (posts, páginas, usuarios)
- Sanitiza todos los parámetros de la REST API
- Bloquea patrones de ataque conocidos (SQL injection, XSS)
#Paso 1: Deshabilitar endpoints innecesarios
Añade este código a tu functions.php:
// Seguridad: Ocultar usuarios, posts y páginas en REST API
add_filter( 'rest_endpoints', function( $endpoints ) {
// Ocultar usuarios (Enumeración)
if ( isset( $endpoints['/wp/v2/users'] ) ) {
unset( $endpoints['/wp/v2/users'] );
}
if ( isset( $endpoints['/wp/v2/users/(?P<id>[\\d]+)'] ) ) {
unset( $endpoints['/wp/v2/users/(?P<id>[\\d]+)'] );
}
// Ocultar listado de Posts y Páginas
if ( isset( $endpoints['/wp/v2/posts'] ) ) {
unset( $endpoints['/wp/v2/posts'] );
}
if ( isset( $endpoints['/wp/v2/posts/(?P<id>[\\d]+)'] ) ) {
unset( $endpoints['/wp/v2/posts/(?P<id>[\\d]+)'] );
}
if ( isset( $endpoints['/wp/v2/pages'] ) ) {
unset( $endpoints['/wp/v2/pages'] );
}
if ( isset( $endpoints['/wp/v2/pages/(?P<id>[\\d]+)'] ) ) {
unset( $endpoints['/wp/v2/pages/(?P<id>[\\d]+)'] );
}
return $endpoints;
});#Paso 2: Sanitizar parámetros de la REST API
Este código detecta y bloquea intentos de inyección SQL y XSS:
/**
* Sanitizar parámetros de la REST API para prevenir inyecciones
*/
add_filter('rest_request_before_callbacks', function($response, $handler, $request) {
$params = $request->get_params();
foreach ($params as $key => $value) {
// Validar nombres de parámetros
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $key)) {
return new WP_Error(
'invalid_param',
'Parámetro no válido detectado.',
array('status' => 400)
);
}
if (is_string($value)) {
// Patrones de SQL injection
$sql_patterns = array(
'/(\\bunion\\b.*\\bselect\\b)/i',
'/(\\bselect\\b.*\\bfrom\\b)/i',
'/(\\binsert\\b.*\\binto\\b)/i',
'/(\\bdelete\\b.*\\bfrom\\b)/i',
'/(\\bdrop\\b.*\\btable\\b)/i',
'/(\\bupdate\\b.*\\bset\\b)/i',
'/(\\/\\*.*\\*\\/)/i',
'/(--)/i',
'/(;)/i',
);
foreach ($sql_patterns as $pattern) {
if (preg_match($pattern, $value)) {
return new WP_Error(
'security_blocked',
'Petición bloqueada por seguridad.',
array('status' => 403)
);
}
}
// Patrones de XSS
$xss_patterns = array(
'/(<script)/i',
'/(javascript:)/i',
'/(onclick)/i',
'/(onerror)/i',
'/(onload)/i',
);
foreach ($xss_patterns as $pattern) {
if (preg_match($pattern, $value)) {
return new WP_Error(
'security_blocked',
'Petición bloqueada por seguridad.',
array('status' => 403)
);
}
}
}
}
return $response;
}, 10, 3);#Paso 3: Protecciones adicionales recomendadas
Añade estas protecciones extra:
// Deshabilitar XML-RPC (otro vector de ataque)
add_filter('xmlrpc_enabled', '__return_false');
// Ocultar usuarios en el sitemap
add_filter( 'wp_sitemaps_users_enabled', '__return_false' );
// Cabeceras de seguridad HTTP
function mi_tema_security_headers() {
if (!is_admin()) {
header('X-XSS-Protection: 1; mode=block');
header('X-Frame-Options: SAMEORIGIN');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: strict-origin-when-cross-origin');
}
}
add_action('send_headers', 'mi_tema_security_headers');
#Verificar que la protección funciona
Después de implementar el código, verifica que los endpoints ya no son accesibles:
- Visita
https://tu-sitio.com/wp-json/wp/v2/posts - Deberías ver un error 404 o un mensaje de «rest_no_route»
- Ejecuta un nuevo análisis de vulnerabilidades para confirmar
#Consideraciones importantes
⚠️ Antes de aplicar estos cambios:
- Si usas plugins que dependen de la REST API (como editores de bloques, apps móviles, o integraciones), podrían dejar de funcionar
- El editor Gutenberg necesita la REST API – si lo usas, considera bloquear solo los endpoints de usuarios
- Haz una copia de seguridad antes de modificar el
functions.php
