He perdido la cuenta de las veces que un cliente ha venido a nosotros con alguna variación de la misma historia: "Nuestra web de WordPress fue hackeada. La limpiamos. Volvieron a hackearla. Se acabó." El último fue una empresa de ecommerce de mediano tamaño cuya tienda WooCommerce había sido infectada con un skimmer de tarjetas de crédito escondido dentro de una actualización de plugin de aspecto legítimo. Llevaban tres semanas filtrando datos de pago de clientes antes de que alguien lo notara. Eso no es un caso extremo. Eso es un martes cualquiera en el ecosistema de WordPress.

Este artículo no pretende atacar a WordPress. Impulsó una enorme parte de la web por buenas razones. Pero la arquitectura que lo hizo accesible en 2005 es la misma que lo convierte en un imán para ataques automatizados en 2025. Si has sido hackeado — o estás harto de gastar dinero en plugins de seguridad que son en sí mismos vectores de ataque — aquí tienes tu guía para migrar a algo fundamentalmente más seguro.

Tabla de Contenidos

¿WordPress hackeado? Por qué migrar a Next.js + Supabase es tu mejor solución

El Problema de Seguridad de WordPress Es Arquitectónico

Quiero dejar algo claro: el equipo de seguridad del núcleo de WordPress hace un trabajo sólido. El núcleo de WordPress, mantenido al día, es razonablemente seguro. Pero nadie ejecuta únicamente el núcleo de WordPress. El sitio de WordPress promedio tiene entre 20 y 30 plugins instalados. Cada uno es una dependencia que tú no escribiste, mantenida por alguien que no conoces, con acceso a tu base de datos, tu sistema de archivos y los datos de tus usuarios.

Lo que se sigue pasando por alto en los artículos de "mejores prácticas de seguridad en WordPress" es esto: el problema no es que los propietarios de sitios WordPress sean negligentes. El problema es que la arquitectura de WordPress te obliga a instalar código PHP ejecutable de terceros directamente en tu servidor para obtener funcionalidades básicas. Es el equivalente a darle a cada contratista que trabaja en tu casa una copia de tu llave — de forma permanente.

La base de datos de vulnerabilidades de WPScan registró más de 7.900 nuevas vulnerabilidades de WordPress en 2024, con los plugins representando aproximadamente el 96% de ellas. El informe de amenazas de Sucuri de 2024 encontró que WordPress representó aproximadamente el 95% de todas las infecciones de CMS que limpiaron. Y Patchstack informó que el 33% de las vulnerabilidades críticas de WordPress en 2024 no tenían parche disponible en el momento de la divulgación.

Estos no son errores que puedan solucionarse con mejores prácticas de codificación. Son propiedades emergentes de la propia arquitectura.

Vectores de Ataque Comunes en WordPress en 2025

Antes de hablar de la solución, cataloguemos contra qué te estás defendiendo realmente. He realizado el triaje de docenas de sitios WordPress comprometidos personalmente, y los ataques siguen patrones predecibles.

Inyección SQL a Través de Plugins

WordPress utiliza una base de datos MySQL con un esquema bien documentado. Cada plugin que acepta entradas del usuario y accede a la base de datos es un punto potencial de inyección SQL. La función $wpdb->prepare() existe, pero es opcional. Los desarrolladores de plugins la olvidan, la usan incorrectamente o la omiten por completo en consultas "simples".

En cierta ocasión rastreé una inyección hasta un plugin de formulario de contacto que había sido abandonado durante 18 meses pero que seguía instalado en más de 200.000 sitios. El atacante usaba una inyección basada en UNION para volcar la tabla wp_users, obtener los hashes de contraseñas de administrador y descifrar los más débiles sin conexión.

-- Así es como se ve típicamente una inyección SQL en WordPress en los registros
GET /wp-content/plugins/vulnerable-plugin/ajax.php?id=1%20UNION%20SELECT%201,user_login,user_pass,4,5%20FROM%20wp_users--

Inyección de Objetos PHP y Ejecución Remota de Código

El uso intensivo de serialize() y unserialize() en WordPress crea oportunidades para la inyección de objetos PHP. Cuando un plugin deserializa datos controlados por el usuario (y muchos lo hacen), un atacante puede elaborar payloads que ejecuten código arbitrario durante el proceso de deserialización.

En 2024, una vulnerabilidad crítica de RCE en un popular plugin de copias de seguridad (instalado en más de 5 millones de sitios) permitió a atacantes no autenticados ejecutar código PHP arbitrario. La corrección tardó 11 días en publicarse. Once días en los que cada sitio con ese plugin era un blanco fácil.

Ataques a la Cadena de Suministro de Plugins

Este es el que más me preocupa. Los atacantes compran plugins abandonados con grandes bases de instalación, publican una "actualización de seguridad" que contiene una puerta trasera, y los mecanismos de actualización automática de WordPress distribuyen el malware a cada sitio que ejecuta ese plugin. Ocurrió con Display Widgets (300.000 instalaciones) y Social Warfare (70.000 instalaciones), y esos son solo los que fueron detectados.

Ataques de Fuerza Bruta en wp-login.php

Cada sitio de WordPress expone /wp-login.php y /xmlrpc.php por defecto. Las botnets automatizadas golpean estos endpoints constantemente. Wordfence informó haber bloqueado un promedio de 3.000 millones de solicitudes maliciosas al mes en toda su red en 2024. Incluso con limitación de tasa y autenticación de dos factores, estás gastando recursos del servidor procesando estos ataques.

Cross-Site Scripting (XSS) a Través de Temas y Plugins

El XSS almacenado en WordPress es especialmente peligroso porque el panel de administración y el sitio público comparten el mismo contexto de sesión. Un payload XSS inyectado a través de un comentario, un envío de formulario o una configuración de plugin vulnerable puede escalar hasta obtener acceso completo de administrador.

Por Qué la Arquitectura Headless Elimina Categorías Enteras de Ataques

Aquí es donde las cosas se ponen interesantes. Una arquitectura headless no solo reduce tu superficie de ataque — elimina categorías enteras de ataques al eliminar las condiciones que los hacen posibles.

En una configuración tradicional de WordPress, el mismo servidor que renderiza tu HTML también:

  • Ejecuta código PHP de más de 20 plugins de terceros
  • Gestiona la autenticación de usuarios
  • Se conecta a la base de datos
  • Sirve la interfaz de administración
  • Gestiona las subidas de archivos
  • Procesa los envíos de formularios

Eso son muchas responsabilidades para una sola aplicación. En una configuración headless con Next.js y Supabase, estas responsabilidades están separadas en servicios aislados:

  • Frontend (Next.js en Vercel/Netlify): HTML/JS estático servido desde una CDN. Sin entorno de ejecución del lado del servidor expuesto a la internet pública en la mayoría de los casos.
  • Base de datos + Auth (Supabase): Postgres gestionado con Row Level Security, nunca expuesto directamente a los usuarios finales.
  • Capa API: Funciones serverless con endpoints explícitos y mínimos.
  • CMS (si es necesario): CMS headless ejecutándose en su propia infraestructura aislada.

No hay PHP en el que inyectar. No hay un directorio de plugins con acceso de escritura. No hay sesión compartida entre el administrador y el sitio público. No hay wp-login.php para que los bots lo bombardeen.

No necesitas un WAF para proteger una superficie de ataque que no existe.

¿WordPress hackeado? Por qué migrar a Next.js + Supabase es tu mejor solución - arquitectura

Next.js + Supabase: Un Stack con la Seguridad como Prioridad

Seamos específicos sobre por qué esta combinación en particular funciona tan bien desde el punto de vista de la seguridad.

Next.js: El Frontend Que No Ejecuta Código

Cuando construyes un sitio Next.js con generación estática (SSG) o regeneración estática incremental (ISR), lo que se despliega son archivos HTML, CSS y JavaScript en una CDN. No hay un servidor de aplicaciones procesando solicitudes en tiempo real. No se puede hacer SQL injection a una CDN.

Para la funcionalidad dinámica, los Server Actions y los Route Handlers de Next.js se ejecutan como funciones serverless. Cada función está:

  • Aislada en su propio contexto de ejecución
  • Sin estado (sin memoria compartida entre solicitudes)
  • De vida corta (arranque en frío, ejecución, terminación)
  • Definida explícitamente (sin descubrimiento automático de endpoints)
// Route Handler de Next.js -- explícito, tipado, mínimo
import { createClient } from '@/lib/supabase/server'
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'

const ContactSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  message: z.string().min(10).max(5000),
})

export async function POST(request: NextRequest) {
  const body = await request.json()
  const parsed = ContactSchema.safeParse(body)
  
  if (!parsed.success) {
    return NextResponse.json({ error: 'Invalid input' }, { status: 400 })
  }
  
  const supabase = await createClient()
  const { error } = await supabase
    .from('contact_submissions')
    .insert(parsed.data)
  
  if (error) {
    return NextResponse.json({ error: 'Submission failed' }, { status: 500 })
  }
  
  return NextResponse.json({ success: true })
}

Compara eso con un plugin de formulario de contacto de WordPress que tiene que engancharse al sistema de acciones de WordPress, incluir su propio manejador AJAX, gestionar su propia verificación de nonce y construir sus propias consultas SQL. La versión de Next.js tiene menos piezas móviles, entrada validada mediante Zod y consultas parametrizadas a través del cliente de Supabase.

Supabase: Postgres con Row Level Security

Supabase te ofrece una base de datos PostgreSQL gestionada con una característica de seguridad clave: Row Level Security (RLS). En lugar de confiar en el código de tu aplicación para aplicar el control de acceso (el modelo de WordPress), defines políticas de seguridad a nivel de base de datos.

-- Solo los usuarios autenticados pueden leer sus propios datos
CREATE POLICY "Users can view own profile"
  ON profiles
  FOR SELECT
  USING (auth.uid() = user_id);

-- El público puede insertar en contact_submissions pero no leer
CREATE POLICY "Anyone can submit contact form"
  ON contact_submissions
  FOR INSERT
  WITH CHECK (true);

CREATE POLICY "Only admins can read submissions"
  ON contact_submissions
  FOR SELECT
  USING (auth.jwt() ->> 'role' = 'admin');

Incluso si un atacante encuentra la manera de realizar consultas arbitrarias en Supabase (lo cual ya es mucho más difícil sin un contexto de ejecución PHP), las políticas de RLS le impiden acceder a datos que no debería ver. Esto es defensa en profundidad que WordPress fundamentalmente no puede ofrecer porque su sistema de permisos está implementado en código PHP, no a nivel de base de datos.

Supabase también gestiona la autenticación con soporte integrado para email/contraseña, magic links, proveedores OAuth y autenticación multifactor. Sin plugin necesario. Sin código de terceros ejecutándose en tu servidor.

Comparación de Superficie de Ataque: WordPress vs Headless

Pongamos esto en perspectiva comparativa.

Vector de Ataque WordPress Next.js + Supabase
Inyección SQL Alto riesgo — los plugins construyen consultas sin procesar Casi nulo — consultas parametrizadas vía cliente Supabase, RLS como respaldo
PHP / Ejecución Remota de Código Alto riesgo — los plugins ejecutan PHP en el servidor No aplica — sin entorno de ejecución PHP
Cadena de Suministro de Plugins Riesgo crítico — las actualizaciones automáticas distribuyen malware No aplica — sin ecosistema de plugins
Fuerza Bruta (login) Siempre expuesto (wp-login.php, xmlrpc.php) Acceso de administrador a través de Supabase Auth o panel separado, sin endpoint de login público necesario
XSS (Almacenado) Alto riesgo — contexto compartido entre admin y sitio público Bajo riesgo — React escapa la salida por defecto, admin y sitio público son aplicaciones separadas
Explotación de Subida de Archivos Alto riesgo — los archivos PHP subidos pueden ejecutarse Bajo riesgo — las subidas van al almacenamiento de objetos (Supabase Storage/S3), nunca se ejecutan como código
Exposición de la Base de Datos Acceso directo a MySQL si el servidor es comprometido Base de datos detrás de la infraestructura de Supabase, políticas RLS como última barrera
DDoS en el Origen El servidor debe procesar cada solicitud Activos estáticos en CDN, el origen rara vez recibe impactos
Enumeración de Rutas Conocidas wp-admin, wp-content, wp-includes son todos rastreables Sin rutas predecibles, sin rutas de administración expuestas

La Guía de Migración: WordPress a Next.js + Supabase

Muy bien, estás convencido (o tu sitio hackeado te convenció). Así es como hacerlo realmente. En Social Animal hemos realizado esta migración suficientes veces como para tener un proceso repetible.

Fase 1: Triaje y Auditoría de Contenido (Semana 1)

Antes de tocar ningún código, necesitas entender qué tienes realmente.

  1. Exporta todo el contenido de WordPress usando WP-CLI o la REST API. No dependas de las exportaciones XML — pierden campos meta y tipos de contenido personalizados.
  2. Cataloga toda la funcionalidad proporcionada por los plugins. Haz una hoja de cálculo: nombre del plugin, qué hace, si realmente lo necesitas y qué lo reemplaza.
  3. Mapea las estructuras de URL para preservar el SEO. Cada URL existente necesita una redirección o una ruta equivalente en Next.js.
  4. Identifica las funcionalidades dinámicas — formularios, búsqueda, cuentas de usuario, ecommerce — que necesitan endpoints de API.
# Exportar contenido de WordPress vía REST API
curl -s "https://yoursite.com/wp-json/wp/v2/posts?per_page=100&page=1" | jq '.' > posts_page1.json
curl -s "https://yoursite.com/wp-json/wp/v2/pages?per_page=100" | jq '.' > pages.json
curl -s "https://yoursite.com/wp-json/wp/v2/media?per_page=100" | jq '.' > media.json

Fase 2: Esquema de Supabase y Migración de Datos (Semana 2)

Diseña tu esquema de base de datos en Supabase basándote en la auditoría de contenido. No te limites a replicar el esquema de WordPress — está sobrecargado con tablas de metadatos y blobs de datos serializados.

-- Esquema limpio y construido con un propósito específico
CREATE TABLE posts (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  title TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  content TEXT,
  excerpt TEXT,
  featured_image TEXT,
  status TEXT DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')),
  published_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  author_id UUID REFERENCES auth.users(id)
);

-- Habilitar RLS de inmediato
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Published posts are public"
  ON posts FOR SELECT
  USING (status = 'published');

CREATE POLICY "Authors can manage own posts"
  ON posts FOR ALL
  USING (auth.uid() = author_id);

Escribe un script de migración (Node.js o Python) que transforme las exportaciones JSON de WordPress en tu nuevo esquema e inserte los datos en Supabase.

Fase 3: Construcción de Next.js (Semanas 3-5)

Construye tu frontend en Next.js. Si trabajas con un equipo que conoce el stack, esto va rápido. Si necesitas ayuda, nuestro equipo de desarrollo Next.js ha realizado esta migración suficientes veces como para tener opiniones firmes sobre los patrones correctos.

Decisiones arquitectónicas clave:

  • Generación estática para páginas de contenido — publicaciones de blog, páginas de aterrizaje, páginas "sobre nosotros". Estas se convierten en archivos HTML en una CDN.
  • Server components para datos dinámicos — obtener datos de Supabase en el momento de la solicitud con caché.
  • Route handlers para envíos de formularios — formularios de contacto, suscripciones a boletines, etc.
  • Middleware para redirecciones — gestiona todas tus antiguas URLs de WordPress.
// next.config.ts -- gestionar redirecciones de URLs de WordPress
const nextConfig = {
  async redirects() {
    return [
      {
        source: '/category/:slug',
        destination: '/blog/category/:slug',
        permanent: true,
      },
      {
        source: '/:year(\\d{4})/:month(\\d{2})/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
      // wp-login.php -- enviar bots a un 410
      {
        source: '/wp-login.php',
        destination: '/gone',
        permanent: true,
      },
      {
        source: '/wp-admin/:path*',
        destination: '/gone',
        permanent: true,
      },
    ]
  },
}

Fase 4: Pruebas y Validación SEO (Semana 6)

  • Ejecuta Screaming Frog contra el nuevo sitio para verificar que cada URL antigua se resuelve o redirige.
  • Valida que los datos estructurados (JSON-LD) estén presentes en todas las páginas.
  • Prueba todos los formularios y funcionalidades dinámicas.
  • Ejecuta comprobaciones de Lighthouse y Core Web Vitals — casi con certeza verás mejoras ya que ahora sirves desde una CDN.
  • Configura la monitorización con Vercel Analytics o tu herramienta preferida.

Fase 5: Lanzamiento y Cambio de DNS (Semana 6-7)

Despliega en Vercel o Netlify, actualiza el DNS y configura la monitorización. Mantén la antigua instancia de WordPress fuera de línea pero accesible durante 30 días por si necesitas consultar algo.

Si tu sitio tiene tráfico significativo o funcionalidad de ecommerce, considera una integración con un CMS headless para la gestión de contenido y habla con nosotros sobre Astro como frontend alternativo para sitios con mucho contenido donde el rendimiento de la compilación importa.

Refuerzo de Seguridad Post-Migración

Una vez que estés en el nuevo stack, aquí tienes tu lista de verificación de seguridad:

  1. Habilita Supabase RLS en cada tabla. Sin excepciones. Si una tabla no tiene políticas, o es inaccesible (buen valor por defecto) o está completamente abierta (malo).
  2. Usa variables de entorno para todos los secretos. Vercel y Netlify gestionan esto bien. Nunca confirmes claves de API.
  3. Configura copias de seguridad de la base de datos en Supabase. La recuperación en un punto del tiempo está disponible en los planes Pro ($25/mes).
  4. Configura las cabeceras de Content Security Policy en tu middleware de Next.js.
  5. Habilita la protección DDoS de Vercel (incluida en todos los planes).
  6. Configura la monitorización de disponibilidad — nosotros usamos Better Uptime, pero Checkly y la monitorización integrada de Vercel también funcionan.
  7. Audita tus políticas de RLS de Supabase trimestralmente. Usa el editor SQL de Supabase para probar las políticas con diferentes contextos de usuario.
// middleware.ts -- cabeceras de seguridad
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  
  response.headers.set('X-Frame-Options', 'DENY')
  response.headers.set('X-Content-Type-Options', 'nosniff')
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
  response.headers.set(
    'Content-Security-Policy',
    "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
  )
  response.headers.set(
    'Permissions-Policy',
    'camera=(), microphone=(), geolocation=()'
  )
  
  return response
}

Coste Real de la Seguridad en WordPress vs Migración

Hablemos de dinero, porque eso es lo que realmente impulsa las decisiones.

Categoría de Coste WordPress (Anual) Next.js + Supabase (Anual)
Alojamiento $300-1.200 (WP gestionado) $0-240 (Vercel Pro)
Plugins de seguridad (Wordfence/Sucuri) $200-500 $0 (no necesarios)
SSL/CDN $0-200 $0 (incluido)
Limpieza de malware (si es hackeado) $500-2.500 por incidente N/A
Tiempo de desarrollador en parches de seguridad $2.000-5.000 $500-1.000
Base de datos (Supabase) Incluida en el alojamiento $0-300 (de nivel gratuito a Pro)
Total $3.000-9.400 $500-1.540

Y eso es antes de tener en cuenta el coste del tiempo de inactividad, la pérdida de confianza de los clientes y las posibles sanciones regulatorias si los datos de los clientes son comprometidos. Una sola brecha notificable bajo el RGPD puede costar decenas de miles en gastos legales y de cumplimiento.

La migración en sí misma suele costar entre $10.000 y $40.000 dependiendo de la complejidad del sitio. Para la mayoría de las empresas, eso se amortiza en 1-2 años solo en reducción del gasto en seguridad — y obtienes un sitio más rápido y fácil de mantener en el proceso. Consulta nuestra página de precios para obtener detalles específicos sobre proyectos de migración.

FAQ

¿Es WordPress realmente tan inseguro, o esto está exagerado?

El núcleo de WordPress está bien mantenido. El problema es que prácticamente nadie ejecuta solo el núcleo. El ecosistema de plugins es donde vive el 96% de las vulnerabilidades, y no puedes ejecutar un sitio WordPress útil sin plugins. No está exagerado — Sucuri limpió malware de más de 60.000 sitios WordPress solo en 2024. La arquitectura te obliga a confiar en código PHP de terceros en tu servidor, y esa confianza se explota constantemente.

¿No puedo simplemente usar mejores plugins de seguridad en lugar de migrar?

Los plugins de seguridad son en sí mismos código PHP que se ejecuta en tu servidor con acceso profundo al sistema. Wordfence y Sucuri están bien mantenidos, pero son parches para un problema arquitectónico. También añaden carga al servidor, pueden entrar en conflicto con otros plugins y han tenido sus propias vulnerabilidades a lo largo de los años. Estás añadiendo complejidad para resolver un problema de complejidad.

¿Cuánto tiempo suele tardar una migración de WordPress a Next.js?

Para un sitio empresarial estándar (10-50 páginas, blog, formularios de contacto), normalmente completamos las migraciones en 5-7 semanas. Los sitios de ecommerce con WooCommerce son más complejos y pueden tardar entre 8 y 14 semanas dependiendo del tamaño del catálogo de productos y la funcionalidad personalizada. Si tienes un sitio más sencillo, ponte en contacto con nosotros y podemos darte un plazo más específico.

¿Perderé mis posiciones en los rankings de SEO al migrar desde WordPress?

No si lo haces correctamente. Los pasos críticos son: preservar todas las estructuras de URL o configurar redirecciones 301 correctas, mantener el marcado de datos estructurados, conservar el contenido intacto y enviar sitemaps actualizados a Google Search Console. La mayoría de nuestros clientes de migración ven mejoras en los rankings en un plazo de 2-3 meses porque las puntuaciones de Core Web Vitals mejoran drásticamente al pasar a un sitio estático servido desde una CDN.

¿Qué pasa con la edición de contenido? WordPress es fácil para usuarios no técnicos.

Es una preocupación legítima. Tienes opciones: Supabase con un panel de administración personalizado, o un CMS headless como Sanity, Contentful o Payload CMS que ofrece a los editores de contenido una experiencia de edición visual similar a la de WordPress. Gestionamos integraciones con CMS headless habitualmente y podemos recomendar la opción más adecuada para las necesidades de tu equipo.

¿Es Supabase lo suficientemente seguro para uso en producción?

Supabase funciona sobre infraestructura de AWS con conformidad SOC 2 Type II. La base de datos subyacente es PostgreSQL, que tiene un sólido historial en materia de seguridad. Las políticas de Row Level Security aplican el control de acceso a nivel de base de datos, lo cual es en realidad más seguro que el sistema de permisos basado en PHP de WordPress. Supabase también ofrece recuperación en un punto del tiempo, conexiones cifradas y restricciones de red en los planes de pago.

¿Qué pasa si mi sitio WordPress ha sido hackeado y necesito ayuda inmediata?

Primero, desconecta el sitio inmediatamente para detener el daño adicional y la fuga de datos. Segundo, preserva la evidencia forense (volcados de base de datos, registros de acceso, instantáneas del sistema de archivos). Tercero, no te limites a limpiarlo y volver a ponerlo en línea — es probable que vuelvas a ser infectado en pocas semanas. Usa el incidente como catalizador para migrar. Contacta con nuestro equipo y podemos ayudarte tanto con el triaje inmediato como con la planificación de la migración.

¿Necesito migrar todo de una vez, o puedo hacerlo de forma incremental?

La migración incremental es posible, pero añade complejidad. Puedes ejecutar Next.js como frontend mientras mantienes WordPress como backend de CMS headless temporalmente y luego eliminar WordPress por completo. Sin embargo, esto significa que WordPress sigue en funcionamiento y sigue necesitando mantenimiento de seguridad durante la transición. Para la mayoría de los sitios, una transición limpia es más rápida, más económica y elimina el riesgo de seguridad antes.