El bloque HTML raw de Gutenberg permite insertar código HTML personalizado directamente en posts, pero sin validación adecuada expone tu sitio a vulnerabilidades XSS almacenadas. La vulnerabilidad más crítica se registró en el Avatar Block de Gutenberg en 2022 (CVSS 8.7), y el problema de fondo sigue vigente: la documentación de Gutenberg sobre escaping de HTML es confusa, y los desarrolladores usan funciones inseguras sin saberlo.

En 30 segundos

  • Gutenberg tiene un bloque HTML que acepta código raw sin escape automático si no lo configurás bien
  • Las vulnerabilidades XSS en Gutenberg son reales: Avatar Block (2022, CVSS 8.7), Template Part Block (2023), y más detectadas por Wordfence
  • Diferencia crítica: RawHTML no escapa, escape-html escapa todo, decodeEntities simplemente decodifica — cada uno tiene un caso de uso diferente
  • Tenés que validar entrada, escapar output, sanitizar atributos — todo en el mismo bloque, o se colapsa la seguridad
  • Solución para admins: deshabilitar el bloque HTML para colaboradores/autores, usar Wordfence para monitoreo, auditar posts existentes

¿Qué es el bloque HTML raw en Gutenberg?

El bloque HTML raw en Gutenberg es un componente que permite a editores insertar código HTML personalizado directamente en un post. No se trata de una medida peligrosa por defecto — tiene casos de uso legítimos: migraciones desde shortcodes legacy, embeds personalizados que no cubre Gutenberg, estructuras HTML complejas que los bloques estándar no permiten.

Pero acá está el problema. Ponele que tenés un post que alguna vez usó un shortcode tipo [mi-personalizado] que hacía cosas raras. En vez de reescribir todo, metés un bloque HTML raw con esa estructura. Lo que no ves es que ese HTML puede contener scripts inline, atributos on* sin escapar, o referencias a elementos que no existen (spoiler: cuando alguien edita ese post después, hace click sin pensar y de repente ejecuta código que no debería ejecutarse).

Gutenberg por diseño no escapa automáticamente el HTML en el bloque raw. Eso es intencional — si escapara todo, no servía para nada. Pero significa que el desarrollador (el que crea un bloque personalizado con HTML raw) tiene que hacer el escaping manualmente. Y ahí es donde empieza la cagada.

Riesgos de seguridad: vulnerabilidades XSS en Gutenberg

Las vulnerabilidades XSS en Gutenberg son documentadas y reales. Wordfence registró una vulnerabilidad crítica en el Avatar Block (Gutenberg 12.9.0 a 18.0.0, CVSS 8.7) donde editores podían inyectar JavaScript a través del atributo de imagen. El Template Part Block tuvo problemas similares. En ambos casos, el bloque asumía que el atributo HTML era «confiable» (que no lo era).

¿Qué pasó? Exactamente lo que no querés: un editor hace click en «Editar Avatar Block», pega un atributo `onerror=»alert(document.cookie)»`, guarda el post, y cada usuario que ve ese post ejecuta ese JavaScript. Si el atacante es más sofisticado, roba cookies de sesión, redirige a phishing, o inyecta malware.

Lo grave es que XSS almacenado en Gutenberg afecta a todos los visitantes del sitio, no solo a editores. Un atacante con permisos de Editor (o Colaborador, dependiendo de la configuración) puede comprometer la experiencia de todos tus usuarios.

La causa de fondo: Gutenberg no tiene una lista explícita de atributos HTML «permitidos» para cada bloque. Los desarrolladores de bloques personalizados cargan lo que el cliente les pide, sin sanitizar. Y si el bloque usa RawHTML sin validación previa, todo el HTML se ejecuta tal cual.

Diferencia entre RawHTML, escape-html y decodeEntities

Estas tres funciones hacen cosas completamente distintas, pero el nombre no lo deja claro. Por eso hay tanta confusión en la documentación de Gutenberg.

Función Qué hace Cuándo usarla Seguro?
RawHTML Renderiza HTML tal cual, sin escape. Las entidades HTML se decodifican automáticamente. Cuando el HTML ya fue validado y sanitizado antes de llegar a RawHTML. Raramente. No, salvo que hayas sanitizado a mano todo lo que entra.
escape-html (o wp.htmlEntities) Convierte caracteres especiales en entidades HTML: < en lugar de <, " en ", etc. Nada se ejecuta. Cuando necesitás mostrar HTML como texto (no renderizarlo). Si querés que el usuario vea el código fuente literalmente. Sí. El contenido no se interpreta, se muestra como string.
decodeEntities Lo opuesto a escape-html: convierte entidades de vuelta a caracteres. < vuelve a <. Cuando necesitás decodificar entidades pero no renderizar HTML. Rara situación. Depende. Si después pasás el resultado a RawHTML, no es seguro. Si lo usás solo para comparación de strings, sí.
bloque html raw gutenberg wordpress diagrama explicativo

La fila que causa el 90% de los problemas: RawHTML. Los desarrolladores leen el nombre, piensan «ah, para HTML raw», meten ahí lo que sea, y boom — XSS. Pero RawHTML no valida el HTML. Solo lo renderiza. Si pasás un <script> adentro, se ejecuta. En nuestro artículo sobre defensas en capas profundizamos sobre esto.

La regla: si usás RawHTML, tenés que sanitizar antes de pasarle el contenido. Y sanitizar correctamente es complicado — tiene que ser una lista blanca de etiquetas permitidas y atributos, no una lista negra de lo «peligroso» (porque siempre hay vectores que no se te ocurren).

Cómo desarrollar bloques Gutenberg seguros con HTML personalizado

Si tenés que permitir HTML personalizado en un bloque, hay un camino correcto. No es complicado, pero no es lo que hace la gente por defecto.

Paso 1: Validar entrada. Cuando el usuario guarda un atributo que va a contener HTML, validá que sea HTML válido. Usá la función wp_kses_post() de WordPress (lado servidor) o, en JavaScript, una librería como sanitize-html. No dejes pasar etiquetas como <script>, <iframe>, o atributos on*.

Paso 2: Escapar output. Cuando renderizás ese HTML en el frontend, usá escape-html por defecto. Si realmente necesitás renderizar HTML (no solo mostrarlo como texto), usa wp.htmlEntities.decodeEntities() despues de wp_kses_post().

Paso 3: Sanitizar atributos. Si el bloque tiene atributos HTML (src, alt, id, class), validalos contra una whitelist. No dejes que metan atributos custom sin revisar. El atributo data-custom se ve inocente, pero si alguien pone data-custom="onerror='...'", pasó la validación pero sigue siendo peligroso.

Ejemplo (pseudo-código):

const sanitized = wp.htmlEntities.decodeEntities(wpKsesList(userHTML, ['p', 'span', 'a', 'strong', 'em'], ['href', 'title', 'class']));
return <RawHTML>{sanitized}</RawHTML>;

Lo que hace este código: permite solo las etiquetas <p>, <span>, <a>, <strong>, <em>, y solo los atributos href, title, class. Todo lo demás se elimina. Después decodifica entidades y renderiza con RawHTML. Cubrimos ese tema en detalle en similar a otros constructores modernos.

Mejores prácticas para administradores: restricción de bloques HTML

Si sos admin de un sitio WordPress con Gutenberg, el control más simple es no permitir el bloque HTML raw a menos que sea absolutamente necesario.

Opción 1: Deshabilitar el bloque HTML para roles específicos. Agregar un snippet en `functions.php`:

add_filter('allowed_block_types_all', function($allowed_blocks) {
  if (! current_user_can('manage_options')) {
    $allowed_blocks = array_filter($allowed_blocks, function($block) {
      return $block !== 'core/html';
    });
  }
  return $allowed_blocks;
});

Esto desactiva el bloque HTML para Editores y Colaboradores. Los Admins lo pueden seguir usando (si saben lo que hacen).

Opción 2: Usar bloques personalizados validados en lugar del bloque HTML raw. Si necesitás embeds custom, creá tu propio bloque que valide entrada internamente. Es más trabajo al principio, pero te ahorrás dolores de cabeza después.

Opción 3: Configurar RankMath para validar contenido en publicación. RankMath (o Yoast) pueden escanear el contenido y flagear si hay scripts inline o atributos sospechosos. No es un reemplazo de validación real, pero es un catch adicional.

Auditoría de seguridad para sitios con HTML raw existente

Si tu sitio ya tiene posts con bloques HTML raw (porque hiciste migraciones o dejaste que los editores hagan lo que quieran), es hora de auditar.

Paso 1: Buscá en la base de datos posts que contengan el bloque HTML raw. La consulta SQL es: Esto se conecta con lo que analizamos en al trabajar con formularios avanzados.

SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<!-- wp:html -->%' AND post_status='publish';

Esto te devuelve todos los posts publicados que contienen bloques HTML raw. Cuidado: pueden ser cientos si tuviste migración masiva.

Paso 2: Revisá manualmente (o con un script) cada bloque HTML raw buscando patrones peligrosos: <script>, atributos on*, <iframe>, <embed>. Si encontrás algo, editá el post y eliminá o sanitizá el HTML.

Paso 3: Usá WPScan para detectar vulnerabilidades conocidas en Gutenberg. Ejecutá un escaneo full del sitio:

wpscan --url https://tudominio.com --api-token [TU_TOKEN]

Paso 4: Configurá Wordfence para monitoreo continuo. Wordfence tiene un WAF que bloquea patrones XSS comunes, y escanea el sitio en background buscando inyecciones sospechosas.

Plugins para mitigar riesgos del bloque HTML raw

Wordfence Security. El clásico. WAF de aplicación (Application Firewall) que bloquea XSS, SQL injection, etc., antes de que lleguen a WordPress. Escanea el sitio periódicamente buscando malware. Si detecta un bloque HTML con XSS, lo puede bloquear en tiempo real. Versión premium: USD 99/año.

Code Snippets. Plugin que permite agregar código PHP/JS sin tocar functions.php. Útil para agregar los filtros de restricción de bloques que mencioné. Gratuito.

No Gutenberg. Si decís «no necesitamos Gutenberg para nada», podés deshabilitar completamente el editor de bloques y volver al editor clásico. Radical, pero efectivo. Gratuito.

Content Guard (premium). Plugin específico para sanitización de contenido. Revisa todo lo que se guarda en la base de datos y lo limpia de XSS, SQL injection, etc. USD 30/año. Te puede servir nuestra cobertura de para automatizar tu flujo editorial.

Errores comunes

1. «El HTML raw solo es peligroso si lo editan atacantes»

Falso. Cualquier Editor o Colaborador es un «atacante potencial» desde el punto de vista de seguridad. Y si tu sitio fue hackeado (plugin vulnerable, fuerza bruta en admin), el atacante ganó permisos de Editor y puede injectar XSS en bloques HTML raw. No es paranoico asumir que el HTML raw es un vector de ataque.

2. «Escapar con htmlspecialchars es suficiente»

No. `htmlspecialchars()` escapa los caracteres básicos (<, >, «, ‘) pero no detiene todos los vectores XSS. Hay ataques CSS, unicode escaping, y trucos de encoding que pasan por htmlspecialchars. Usá `wp_kses_post()` o una librería de sanitización probada.

3. «Si deshabilito el bloque HTML raw, pierdo funcionalidad»

Verdad, pero ganás seguridad. Y la mayoría de las cosas que hace un bloque HTML raw se pueden hacer con bloques personalizados validados o incluso con shortcodes que escapan entrada. El bloque HTML raw es un atajo de conveniencia, no una necesidad.

Qué está confirmado / Qué no

Hecho Estado Fuente
Gutenberg tiene vulnerabilidades XSS históricas en Avatar Block y Template Part Block Confirmado Wordfence (2022-2023)
RawHTML sin sanitización es inseguro Confirmado GitHub #13156, OWASP
Todos los sitios con bloques HTML raw están comprometidos No confirmado Depende de qué contiene el bloque HTML. Si solo tiene HTML estático sin scripts, no hay riesgo inmediato.
Deshabilitar el bloque HTML raw previene completamente XSS Parcialmente confirmado Previene XSS vía bloque HTML raw, pero XSS puede venir de otros bloques si también tienen bugs.
WordPress versión 6.4+ tiene todo sanitizado por defecto No confirmado WordPress confía en que los desarrolladores de bloques saniticen. La sanitización no es automática.

Preguntas Frecuentes

¿Cuál es la diferencia entre el bloque HTML raw de Gutenberg y el bloque Custom HTML que mencionan algunos tutos?

Son el mismo bloque, solo que en versiones viejas de Gutenberg se llamaba «Custom HTML». Ahora se llama «HTML» y está bajo el panel de «Layout» en el editor. Mismo riesgo, mismo nombre antiguo.

¿Si desactivo el bloque HTML raw, puedo seguir usando shortcodes?

Sí. Los shortcodes de WordPress (tipo

, ) siguen funcionando en Gutenberg. Si creaste shortcodes personalizados y los metiste en bloques HTML raw, podés seguir usándolos directamente en posts como shortcodes, sin el bloque HTML raw.

¿Wordfence bloquea XSS en tiempo real o solo en escaneos programados?

Wordfence tiene dos capas: el WAF (Web Application Firewall) bloquea ataques en tiempo real, antes de que lleguen a WordPress. Los escaneos buscan malware ya inyectado en la base de datos (eso es más lento, se ejecuta en background). Ambas capas son necesarias.

¿Puedo tener un bloque HTML raw para ciertos posts (tipo documentación) pero desactivarlo para posts de blog?

Técnicamente sí, pero es complicado. Tendrías que usar un filtro de WordPress que revise el post_type y active/desactive el bloque según contexto. No vale la pena — mejor restricción por rol de usuario (Admins pueden usarlo, Editores no).

¿Si migro mis posts del bloque HTML raw a bloques estándar de Gutenberg, desaparecen los riesgos?

La mayoría de los riesgos, sí. Los bloques estándar (Paragraph, List, Image, etc.) escapan el contenido automáticamente. Pero hay que hacer la migración bien — si solo convertís HTML raw a un bloque Paragraph metiendo el HTML adentro, seguís con el mismo problema.

Conclusión

El bloque HTML raw de Gutenberg tiene un lugar en el ecosistema WordPress, pero es una herramienta de cuidado. Las vulnerabilidades XSS en Gutenberg son reales, históricamente documentadas, y siguen siendo un riesgo si no validás y escapás correctamente.

Lo que tenés que hacer depende de tu contexto. Si administrás un sitio con múltiples editores: deshabilita el bloque HTML raw para roles que no sean Admin. Si desarrollás un bloque personalizado que usa HTML: sanitizá entrada, escapá output, testeá contra vectores XSS comunes (scripts inline, atributos on*, unicode escaping). Si ya tenés posts con bloques HTML raw: auditá, buscá patrones peligrosos, configurá Wordfence para monitoreo continuo.

No es parálisis por análisis. Es el costo de tener un sitio que no te vuela por los aires.

Fuentes

Categorizado en: