Saltar al contenido

Desarrollo web

Codificación y decodificación de entidades HTML: guía completa para desarrolladores

11 min de lectura

Un usuario pega un fragmento de código en un cuadro de comentarios y de repente todo el diseño de la página se rompe. La causa no es exótica: es un solo carácter < sin escapar que se interpreta como el inicio de una etiqueta HTML. El navegador deja de renderizar el texto del comentario y empieza a buscar nombres de elementos, atributos y corchetes de cierre. La solución es igual de simple: reemplaza ese carácter por su entidad HTML y la página se muestra correctamente. Esta guía explica qué son las entidades HTML, por qué todo desarrollador que maneja contenido generado por el usuario las necesita y cómo evitar los errores comunes que causan problemas reales en producción.

Qué son realmente las entidades HTML

HTML reserva un pequeño conjunto de caracteres para su propia sintaxis: los corchetes angulares definen etiquetas, los ampersands introducen entidades y las comillas envuelven valores de atributos. Cuando necesitas mostrar uno de estos caracteres como texto literal —no como markup— lo reemplazas por una referencia de entidad. Una referencia de entidad es un nombre corto entre ampersand y punto y coma (&amp;lt;) o un código numérico (&#60; o &#x3C;). El navegador interpreta la entidad durante el parseo y muestra el carácter correspondiente sin tratarlo como HTML estructural.

Hay dos familias de entidades. Entidades nombradas usan nombres legibles para humanos — &amp;amp; para ampersand, &amp;lt; para menor que, &amp;gt; para mayor que. Entidades numéricas (decimales) usan el punto de código Unicode del carácter: &#60; para menor que, &#x3C; en hexadecimal. Ambos enfoques producen el mismo resultado en el navegador; las entidades nombradas son más fáciles de leer para los humanos, las entidades numéricas cubren cada carácter Unicode.

Las cinco entidades que todo desarrollador web debe memorizar son: &amp;amp; (&), &amp;lt; (<), &amp;gt; (>), &amp;quot; ("), &amp;apos; ('). La especificación HTML5 también define &amp;nbsp; para un espacio sin salto —la única entidad nombrada que no corresponde a un carácter de sintaxis pero que aparece constantemente en contenido web.

Las cinco entidades obligatorias y sus alternativas numéricas

Entidad nombradaCarácterDecimalHexCuándo se necesita
&amp;amp;&&38;&#x26;Cada ampersand en texto o valores de atributos — se debe escapar primero
&amp;lt;<&#60;&#x3C;Cada signo menor que fuera de una etiqueta; muestra fragmentos de código, comparaciones
&amp;gt;>&#62;&#x3E;Mayor que en texto; requerido junto con &lt; en bloques de código
&amp;quot;"&#34;&#x22;Comillas dobles dentro de valores de atributos HTML
&amp;apos;'&#39;&#x27;Comillas simples dentro de atributos con comillas simples; compatibilidad XML

Escapa los ampersands primero — siempre

El ampersand es el carácter puerta para todas las entidades. Si escapas < primero y dejas el &amp; sin escapar, el navegador puede interpretar tu &amp;lt; recién creado como un ampersand literal seguido de "lt;" en lugar de la entidad &amp;lt;. Siempre codifica &amp; como &amp;amp; antes de tocar cualquier otra cosa. Este es el error de codificación más común en código de producción.

Por qué escapar la entrada del usuario es un requisito de seguridad

Cuando una aplicación almacena texto que un usuario escribió y lo renderiza de vuelta en HTML sin codificar, cualquier corchete angular en ese texto se convierte en parte del DOM. Un comentario malicioso que contiene <script>alert('XSS')</script> no se muestra como texto — se ejecuta como JavaScript. Esto es un ataque de cross-site scripting (XSS), una de las vulnerabilidades web más comunes. El OWASP Top 10 ha incluido XSS en cada edición porque los desarrolladores siguen olvidando codificar la entrada del usuario.

La defensa es sencilla: antes de que cualquier cadena generada por el usuario entre en salida HTML, codifica los cinco caracteres especiales. Cuando la cadena <script> pasa por una codificación correcta, se convierte en &amp;lt;script&amp;gt; y el navegador la muestra como texto visible inofensivo. El mismo principio se aplica a los atributos: si el nombre del usuario contiene comillas dobles y las insertas en un atributo title sin codificar, el atacante puede salir del atributo e inyectar HTML arbitrario.

<!-- PELIGROSO: entrada del usuario inyectada sin codificar -->
<div class="comment">${userComment}</div>

<!-- SEGURO: entrada del usuario codificada antes de la inyección -->
<div class="comment">${encodeHtml(userComment)}</div>

<!-- PELIGROSO: nombre inyectado en atributo sin codificar -->
<img src="photo.jpg" alt="${userName}">

<!-- SEGURO: nombre codificado dentro del atributo -->
<img src="photo.jpg" alt="${encodeHtml(userName)}">

Codificación HTML vs codificación URL: resuelven problemas diferentes

Los desarrolladores frecuentemente confunden la codificación HTML con la codificación URL (también llamada codificación porcentual). Sirven propósitos completamente diferentes. La codificación HTML protege el contenido de texto dentro de un documento HTML para que los caracteres especiales se muestren, no se parseen. La codificación URL protege caracteres dentro de una ruta URL, cadena de consulta o fragmento para que la solicitud HTTP se transmita correctamente.

Un espacio en HTML se convierte en &amp;nbsp; (o simplemente un espacio normal si no necesitas prevenir saltos de línea). El mismo espacio en una URL se convierte en %20. El ampersand en HTML se convierte en &amp;amp; — pero en una cadena de consulta de URL es un separador, así que si necesitas un ampersand literal en un valor de parámetro se convierte en %26. Usar la codificación equivocada en el contexto equivocado rompe las cosas: &amp;nbsp; dentro de una URL no funcionará, y %20 dentro de HTML aparecerá como texto literal.

La regla práctica

Dentro de texto HTML o atributos: usa entidades HTML. Dentro de URLs, rutas de archivo o parámetros de consulta: usa codificación porcentual. Cuando un valor pasa por ambos (por ejemplo, una consulta de búsqueda en el atributo href de un enlace), aplica primero la codificación URL al valor de la consulta y luego codifica HTML el href completo antes de insertarlo en el markup.

Escenarios del mundo real donde la codificación importa

La codificación HTML no es un ejercicio académico. Toda aplicación que muestra contenido generado por el usuario o renderiza datos dinámicos enfrenta estas situaciones a diario:

  • Fragmentos de código en blogs o documentación: Un tutorial que muestra una comparación de Python como if x < 10 and y > 5: romperá el HTML circundante si los corchetes angulares no están codificados. El < inicia una etiqueta, el navegador entra en modo de recuperación de errores y la salida visible sale mutilada.
  • Comentarios de usuario y publicaciones en foros: Toda plataforma que acepta entrada de texto libre —comentarios, reseñas, mensajes de chat— debe codificar la salida. Un comentario que simplemente escribe a < b no es malicioso, pero el < sin escapar de todos modos romperá la visualización.
  • JSON dentro de etiquetas script: JSON renderizado por el servidor y embebido en un bloque <script> requiere manejo cuidadoso. Cadenas que contienen </script> cerrarán la etiqueta script prematuramente. El patrón seguro es codificar < y > dentro de los valores de cadena JSON, o usar un bloque de datos type="application/json" sin parseo JavaScript.
  • Enlaces mailto con caracteres especiales: La línea de asunto de un enlace mailto con & debe codificarse como &amp;amp; dentro del href HTML, mientras que el parámetro de asunto real usa codificación porcentual para espacios y caracteres no ASCII.
  • Ampersands dobles en atributos: Escribir href="search?q=a&&b" directamente en HTML es problemático. El navegador ve el segundo & como el inicio de una referencia de entidad. Ambos ampersands deben codificarse: href="search?q=a&amp;amp;b".
  • Menor que en atributos data: Un atributo data como data-config="{min: 0, max: 10}" no tiene problemas, pero data-template="<div>content</div>" debe codificarse completamente porque < y > serán interpretados por el parser HTML antes de que JavaScript vea el atributo.

Sobre-escapar y sub-escapar: los dos modos de fallo

Obtener la codificación mal en cualquiera de las dos direcciones crea errores visibles. Sub-escapar significa que no codificaste suficientes caracteres. Las consecuencias van desde diseños rotos (un < desbocado roba parte de la página) hasta vulnerabilidades XSS completas. Sub-escapar es la forma de fallo más peligrosa porque afecta la seguridad, no solo la visualización.

Sobre-escapar significa que codificaste caracteres que no necesitaban codificación, o codificaste los mismos caracteres múltiples veces. Un nombre de usuario como Tom & Jerry aparece como Tom &amp;amp; Jerry si el ampersand se codifica dos veces. Esto ocurre cuando una pipeline codifica la misma cadena en dos etapas diferentes — la aplicación codifica una vez, luego un motor de plantillas o capa de seguridad de contenido codifica de nuevo. El resultado es texto ilegible y visualización rota.

La forma de evitar ambos problemas es una regla clara: codifica una vez, en el punto de salida, y nunca antes. Almacena el valor crudo en tu base de datos. Aplica la codificación HTML en el momento exacto en que lo insertas en markup HTML. No codifiques al guardar, no codifiques al pasar entre funciones del backend, y no codifiques al almacenar en una variable de plantilla. La codificación en un solo punto previene la doble codificación; la codificación consistente en la salida previene la sub-codificación.

// MAL: codificar en el momento del almacenamiento
db.save({ comment: encodeHtml(userInput) }); // riesgo de doble codificación

// BIEN: codificar en el punto de salida
const comment = db.getComment(id); // texto crudo
element.innerHTML = encodeHtml(comment); // codificar una vez aquí

Qué caracteres realmente necesitan codificación en cada contexto

No todos los caracteres necesitan codificación en todas las situaciones. El conjunto mínimo depende de dónde aparezca el texto:

  • En contenido de elementos (entre etiquetas de apertura y cierre): Codifica & y <. El > es técnicamente opcional pero fuertemente recomendado: los navegadores toleran > sin escapar en contenido, pero puede causar problemas de parseo en casos límite con secciones CDATA y nodos de texto adyacentes.
  • En atributos entre comillas dobles: Codifica &, < y ". El < rara vez aparece en valores de atributo pero debe escaparse cuando está presente.
  • En atributos entre comillas simples: Codifica &, < y ' en lugar de comillas dobles.
  • En cadenas JavaScript insertadas en HTML: Las cinco entidades obligatorias. Cuando una variable JS fluye hacia innerHTML, template literal o document.write, cada carácter especial debe codificarse.
  • Dentro de secciones CDATA (XML/XHTML): Puedes omitir la codificación de entidades completamente dentro de bloques <![CDATA[...]]> — excepto la secuencia ]]> que nunca debe aparecer sin escapar. CDATA es relevante para SVG y MathML embebidos en documentos HTML5.

Quirks del navegador y casos límite

Los navegadores modernos son permisivos con muchos errores de HTML, pero esa permisividad crea una falsa sensación de seguridad. Casos límite específicos a los que debes prestar atención:

  • Atributos sin comillas con caracteres especiales: Escribir class=foo&bar sin comillas romperá porque el navegador ve & como el inicio de una entidad. Siempre comilla los valores de atributo y codifica el contenido de esas comillas.
  • Los elementos sin etiqueta de cierre: Elementos como <script>, <style> y <textarea> usan un modo de parseo especial. Dentro de estos elementos, casi no ocurre decodificación de entidades en HTML5 — debes codificar < y & para prevenir el cierre prematuro de etiquetas, pero entidades nombradas como &amp;nbsp; se tratan como texto literal.
  • Referencias de entidad inválidas: El navegador descarta silenciosamente los puntos y coma de entidades no reconocidas. Escribir &foo; produce el texto literal &foo; en la mayoría de navegadores, pero &foo (sin punto y coma) puede tragar el siguiente carácter — un bug extremadamente difícil de rastrear.
  • Referencias numéricas de caracteres y caracteres de control: Usar entidades numéricas para caracteres de control como &#x0; (nulo) está prohibido en HTML5. El parser los reemplaza silenciosamente por U+FFFD (carácter de reemplazo) o los descarta completamente.

La trampa del punto y coma

Una referencia de entidad sin punto y coma final es ambigua. &amp;lt sin punto y coma se parsea como la entidad &amp;lt seguida del siguiente carácter que venga. Un usuario que escribe &amp;cat accidentalmente crea la entidad &amp;ca seguida de una t suelta. Asegúrate de que tu función de codificación incluya el punto y coma para cada entidad que produce.

Estrategias de codificación en la práctica

La mayoría de frameworks web incluyen funciones de codificación integradas. El JSX de React codifica automáticamente los valores de expresiones dentro de contenido de elementos (pero dangerouslySetInnerHTML omite esto completamente — úsalo solo con contenido pre-sanitizado y confiable). Los motores de plantillas del lado del servidor como Jinja2, Handlebars y Blade proporcionan filtros de escape. El punto clave: saber dónde tu framework codifica automáticamente y dónde no.

Para las cinco entidades obligatorias, una función de codificación mínima es sencilla. La función reemplaza cada carácter especial con su entidad nombrada, ampersands primero:

function encodeHtml(str) {
  return str
    .replace(/&/g, "&amp;")   // debe ir primero
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

// Uso:
const safe = encodeHtml('He said "a < b & c > d"');
// → He said &quot;a &lt; b &amp; c &gt; d&quot;

Prefiere la biblioteca estándar

La función manual de arriba es para comprender, no para producción. En Node.js usa la API de codificación de texto integrada o una biblioteca bien testeada. En el navegador, la asignación a textContent maneja la mayoría de los casos automáticamente. La API DOMParser es la ruta más segura para HTML proporcionado por el usuario cuando necesitas permitir algunas etiquetas pero bloquear otras — parsea sin ejecutar scripts.

Herramientas de codificación HTML en Convrs

Cuando necesitas codificar o decodificar entidades HTML rápidamente —ya sea para depurar un problema de visualización, preparar contenido para un CMS o probar cómo se verá el texto codificado— la herramienta HTML Encode/Decode de Convrs maneja ambas direcciones en el navegador. Pega tu texto, elige codificar o decodificar y obtén el resultado al instante. Sin subida de archivos, sin ida y vuelta al servidor, sin datos que salgan de tu dispositivo. Cubre las cinco entidades obligatorias más referencias numéricas, para que puedas verificar que tu codificación de salida coincide con lo que el navegador espera.

Buy me a coffee