html entity encode decode

Codificador y Decodificador de Entidades HTML: Gestión Segura de Contenido Web

Codifica y decodifica instantáneamente entidades HTML para prevenir XSS y asegurar que tus caracteres especiales se rendericen perfectamente en todos los navegadores.

¿Qué son las Entidades HTML?

Las entidades HTML son secuencias de texto especiales utilizadas para representar caracteres que tienen un significado reservado en HTML o que no pueden ser fácilmente escritos o transmitidos. Una entidad comienza con un ampersand (&) y termina con un punto y coma (;). Entre esos delimitadores se encuentra un nombre descriptivo (una entidad con nombre como &) o un punto de código numérico (una entidad numérica como & o &).

A primera vista, las entidades parecen un detalle tipográfico menor. En realidad, son una piedra angular de la seguridad web, la internacionalización y la renderización fiable en los navegadores. Todo desarrollador web que trabaje con contenido dinámico (entradas de usuario, datos de CMS, plantillas de correo electrónico o motores de plantillas) debe entender cómo y cuándo codificar entidades HTML.


Una Breve Historia de las Entidades HTML y la Codificación de Caracteres

La Era ASCII (1960s–1980s)

El Código Estándar Americano para el Intercambio de Información (ASCII) definía 128 caracteres: las 26 letras inglesas (mayúsculas y minúsculas), dígitos, puntuación y códigos de control. Esto era suficiente para el inglés americano, pero completamente inadecuado para el resto de los idiomas del mundo.

Latin-1 / ISO-8859-1 (1980s–1990s)

ISO-8859-1 (también llamado Latin-1) extendió ASCII a 256 caracteres utilizando el octavo bit, añadiendo caracteres acentuados utilizados en los idiomas de Europa Occidental (é, ü, ñ, etc.). HTML 2.0 y HTML 3.2 adoptaron formalmente Latin-1 como su conjunto de caracteres de referencia, y las entidades con nombre de HTML para muchos de estos caracteres se definieron en este momento: entidades como é (é), ü (ü) y ñ (ñ).

El problema: 256 caracteres aún no podían cubrir el japonés, árabe, chino, coreano o cientos de otros scripts. Diferentes regiones inventaron codificaciones incompatibles (Shift-JIS, Big5, KOI8-R…), creando el problema "Mojibake": texto ilegible cuando se mezclaban las codificaciones.

Unicode y UTF-8 (1991–Presente)

El Consorcio Unicode publicó su primer estándar en 1991 con el objetivo de asignar un punto de código único a cada carácter en cada sistema de escritura. Hoy en día, Unicode cubre más de 140,000 caracteres en más de 150 alfabetos.

UTF-8, introducido en 1992 por Ken Thompson y Rob Pike, codifica los puntos de código Unicode como 1–4 bytes y es compatible con ASCII. Se convirtió en la codificación dominante para la web en la década de 2000. A partir de 2024, más del 98% de las páginas web utilizan UTF-8.

¿Por qué siguen importando las entidades en un mundo UTF-8?

Si podemos codificar cualquier carácter con UTF-8, ¿por qué siguen existiendo las entidades? Por tres razones:

  1. Caracteres reservados: <, >, y & tienen un significado especial en el marcado HTML. Incluso en documentos UTF-8, debes escaparlos para mostrarlos literalmente.
  2. Delimitadores de atributos: " y ' delimitan los valores de los atributos y deben escaparse dentro de esos valores.
  3. Control de espacios en blanco: &nbsp; (espacio de no ruptura) controla el diseño de formas que los espacios regulares no pueden.

Conceptos Básicos: Cómo Funcionan las Entidades HTML

Entidades con Nombre

Las entidades con nombre son la forma más legible para los humanos, utilizando un nombre mnemotécnico derivado de la descripción del carácter. HTML5 define más de 2,000 entidades con nombre.

<!-- Uso de entidades con nombre -->
<p>Pan &amp; Mantequilla</p>          <!-- muestra: Pan & Mantequilla -->
<p>3 &lt; 5 y 10 &gt; 7</p>         <!-- muestra: 3 < 5 y 10 > 7 -->
<p>Copyright &copy; 2026</p>          <!-- muestra: Copyright © 2026 -->
<p>Precio: 49&euro;</p>                <!-- muestra: Precio: 49€ -->

Entidades Numéricas: Decimales y Hexadecimales

Cualquier carácter Unicode puede ser referenciado por su punto de código en forma decimal o hexadecimal:

  • Decimal: &# seguido del punto de código decimal — ej., &#60; para < (U+003C)
  • Hexadecimal: &#x seguido del punto de código hex — ej., &#x3C; para <

Ambas formas son equivalentes. La hexadecimal es común en la documentación técnica porque los puntos de código Unicode se expresan normalmente en hex (U+003C).

<!-- Las tres son formas equivalentes de mostrar < -->
&lt;
&#60;
&#x3C;

Las 5 Entidades de Seguridad Críticas

Estos cinco caracteres forman la base de la defensa contra la inyección HTML:

Carácter Entidad con Nombre Decimal Hex Contexto
< &lt; &#60; &#x3C; Abre etiquetas HTML
> &gt; &#62; &#x3E; Cierra etiquetas HTML
& &amp; &#38; &#x26; Inicia entidades
" &quot; &#34; &#x22; Atributos con comillas dobles
' &apos; &#39; &#x27; Atributos con comillas simples

Codifica siempre los cinco cuando reflejes la entrada del usuario en HTML.


Tabla de Referencia de Entidades HTML

Carácter Entidad con Nombre Decimal Hex Uso
< &lt; &#60; &#x3C; Delimitadores de etiquetas
> &gt; &#62; &#x3E; Delimitadores de etiquetas
& &amp; &#38; &#x26; Prefijo de entidad
" &quot; &#34; &#x22; Valores de atributos
' &apos; &#39; &#x27; Valores de atributos
&nbsp; &#160; &#xA0; Espacio de no ruptura
© &copy; &#169; &#xA9; Copyright
® &reg; &#174; &#xAE; Marca registrada
&trade; &#8482; &#x2122; Marca comercial
&euro; &#8364; &#x20AC; Signo del euro
&mdash; &#8212; &#x2014; Raya (em dash)
&ndash; &#8211; &#x2013; En dash

Prevención de XSS: Por qué la Codificación Salva tu Sitio

El Cross-Site Scripting (XSS) es una de las vulnerabilidades de seguridad web más frecuentes. Ocurre cuando un atacante inyecta scripts maliciosos en contenido que luego se sirve a otros usuarios. La codificación de entidades HTML es la defensa principal.

El Ataque XSS Clásico

Considera una función de búsqueda que devuelve la consulta del usuario:

<!-- VULNERABLE: insertando directamente la entrada del usuario -->
<p>Buscaste: <?php echo $_GET['q']; ?></p>

Un atacante crea una URL como:

https://example.com/search?q=<script>document.cookie</script>

El navegador renderiza la etiqueta <script> y ejecuta el código del atacante. Con document.cookie, roban tokens de sesión. Con fetch(), exfiltran datos a un servidor controlado por el atacante.

La Solución: Codificar en la Salida

<!-- SEGURO: codificar toda la salida -->
<p>Buscaste: <?php echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); ?></p>

Ahora el navegador ve:

<p>Buscaste: &lt;script&gt;document.cookie&lt;/script&gt;</p>

El script se muestra como texto inofensivo; no se produce ninguna ejecución.


Ejemplos de Código Prácticos

JavaScript: Manipulación Segura del DOM

La forma más segura de insertar contenido generado por el usuario en JavaScript es textContent, que nunca interpreta HTML:

// SEGURO: textContent nunca analiza HTML
const el = document.getElementById('output');
el.textContent = userInput; // escapa automáticamente todo

// PELIGROSO: innerHTML analiza y ejecuta HTML
el.innerHTML = userInput; // NUNCA hagas esto con entrada no confiable

Si debes construir cadenas HTML en JavaScript, escapa siempre primero:

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

const safe = `<p>Buscaste: ${escapeHtml(userInput)}</p>`;

Nota: escapa siempre & primero; si escapas < primero, el & en &lt; se escaparía a su vez en &amp;lt;, causando una doble codificación.

PHP: htmlspecialchars() y htmlentities()

PHP proporciona dos funciones principales para la codificación HTML:

// htmlspecialchars: codifica solo los 5 caracteres críticos
$safe = htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');

// htmlentities: codifica TODOS los caracteres con equivalentes de entidad con nombre
$safe = htmlentities($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');

Diferencia clave: htmlspecialchars() solo codifica <, >, &, " y '. htmlentities() también codifica letras acentuadas y símbolos como é&eacute;. Para documentos UTF-8, normalmente se prefiere htmlspecialchars(): UTF-8 puede representar todos los caracteres directamente; solo los cinco peligrosos necesitan ser escapados.

Pasa siempre ENT_QUOTES para codificar ambos tipos de comillas, y especifica siempre 'UTF-8' como el juego de caracteres.

Python: html.escape()

import html

# Escapado básico
safe = html.escape(user_input)

# Escapar también comillas simples (quote=True es el valor por defecto desde Python 3.2)
safe = html.escape(user_input, quote=True)

# Ejemplo
user_input = '<script>alert("XSS")</script>'
print(html.escape(user_input))
# Salida: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

Plantillas HTML (Jinja2, Django, Handlebars)

La mayoría de los sistemas de plantillas modernos auto-escapan por defecto:

<!-- Jinja2 / Django: auto-escapado por defecto -->
<p>{{ user_comment }}</p>

<!-- Renderizar HTML crudo deliberadamente (PELIGROSO si es controlado por el usuario): -->
<p>{{ user_comment | safe }}</p>

<!-- Handlebars: las llaves dobles escapan, las triples no -->
<p>{{userComment}}</p>    <!-- escapado — seguro -->
<p>{{{userComment}}}</p>  <!-- HTML crudo — ¡peligroso! -->

Casos de Uso en el Mundo Real

1. Documentación Técnica y Blogs de Código

Al escribir sobre HTML, frecuentemente necesitas mostrar ejemplos de código que contienen <, >, y &. Las entidades te permiten mostrar estos como caracteres literales sin romper la estructura de la página:

<pre><code>
Usa &lt;div&gt; y &lt;/div&gt; para envolver secciones.
El carácter &amp; inicia una entidad HTML.
</code></pre>

2. CMS y Contenido Generado por el Usuario

Cualquier CMS que almacene y muestre texto generado por el usuario debe codificar las entidades HTML antes de enviarlo a la página. Esto incluye comentarios de blog, publicaciones en foros, reseñas de productos y publicaciones en redes sociales. No hacerlo es responsable de una gran proporción de incidentes de XSS en el mundo real.

3. Plantillas de Correo Electrónico HTML

Los clientes de correo electrónico son notoriamente inconsistentes. El uso de entidades con nombre para caracteres tipográficos (&mdash;, &lsquo;, &rsquo;, &hellip;) ayuda a asegurar una renderización correcta en Gmail, Outlook, Apple Mail y clientes antiguos como Outlook 2007 (que utiliza el motor de renderizado de Word).

4. Tipografía y Símbolos Especiales

Las entidades proporcionan un acceso fiable a caracteres tipográficos que son difíciles de escribir o que pueden no sobrevivir al copiar y pegar entre sistemas:

<p>La raya&mdash;utilizada para incisos&mdash;es más expresiva que un guion.</p>
<p>Ella dijo &ldquo;hola&rdquo; y sonrió.</p>
<p>Precio: 29&nbsp;&euro;</p>
<!-- &nbsp; evita que "29" y "€" se separen en líneas distintas -->

5. Internacionalización en Sistemas Heredados

En sistemas antiguos que no pueden manejar UTF-8 de manera fiable, las entidades numéricas permiten codificar cualquier carácter Unicode:

<!-- Carácter chino para "dragón" (U+9F99) como entidad decimal -->
&#40857;

<!-- Hiragana japonés あ (U+3042) -->
&#12354;

Entidades con Nombre vs. Numéricas: Comparación

Aspecto Con Nombre (&lt;) Decimal (&#60;) Hexadecimal (&#x3C;)
Legibilidad Alta Media Baja
Cobertura ~2,000 chars Todo Unicode Todo Unicode
Soporte HTML5 Completo Completo Completo
Soporte XML Solo 5 predefinidas Completo Completo
Ideal para Caracteres comunes Cualquier Unicode Refs técnicas/Unicode

HTML vs. XML: Una Diferencia Crítica

XML solo predefine 5 entidades (&lt;, &gt;, &amp;, &quot;, &apos;). Todas las demás entidades con nombre como &copy; o &nbsp; están indefinidas en XML a menos que se declaren en una DTD.

<!-- INVÁLIDO en XML (entidad no definida): -->
<p>Copyright &copy; 2026</p>

<!-- VÁLIDO en XML (la entidad numérica funciona en todas partes): -->
<p>Copyright &#169; 2026</p>

<!-- VÁLIDO en HTML5 (ambas funcionan): -->
<p>Copyright &copy; 2026</p>

Si estás escribiendo XHTML o SVG, usa entidades numéricas para cualquier cosa más allá de las 5 básicas, o usa el carácter literal UTF-8 directamente.


Mejores Prácticas

1. Usa UTF-8 en todo tu Stack

Declara UTF-8 en todas partes: la colación de la base de datos, el encabezado HTTP Content-Type y la etiqueta HTML <meta charset>. Esto elimina la necesidad de codificar caracteres no ASCII con entidades.

<meta charset="UTF-8">
header('Content-Type: text/html; charset=UTF-8');

2. Codifica de Acuerdo al Contexto

Diferentes contextos de inyección requieren diferentes estrategias de escapado:

  • Cuerpo HTML: codifica <, >, &
  • Atributos HTML: codifica <, >, &, ", '
  • Cadenas JavaScript: usa escapado \uXXXX o codificación JSON
  • Valores CSS: se aplican reglas de escapado diferentes
  • URLs: usa codificación porcentual (%3C no &lt;)

Un carácter codificado para un contexto no es necesariamente seguro en otro.

3. Codifica en la Salida, no en la Entrada

Almacena los datos crudos en tu base de datos. Codifica al salir a HTML. Si codificas en la entrada, arriesgas una doble codificación en la salida, y los datos se corrompen en contextos que no son HTML (APIs JSON, correos de texto plano, etc.).

4. Nunca Decodifiques Entrada no Confiable antes del Procesamiento

Decodificar entidades suministradas por el usuario antes de aplicar filtros de seguridad derrota el propósito. &#60;script&#62; decodificado se convierte en <script>, un bypass clásico de los filtros ingenuos que "bloquean paréntesis angulares".

5. Evita la Doble Codificación

La doble codificación (&amp;lt; que se renderiza como &lt;, no como <) es un error común cuando múltiples capas de la aplicación codifican de forma independiente. Centraliza tu codificación en una única capa de presentación.

6. Recuerda que &apos; no estaba en HTML4

La entidad &apos; está definida en XML y XHTML pero no estaba definida en HTML4. En entornos HTML4, usa &#39; en su lugar. HTML5 añadió oficialmente &apos; a la lista de entidades con nombre.


Preguntas Frecuentes

P: ¿Necesito codificar cada carácter especial, o solo los peligrosos?

Por seguridad, debes codificar al menos los 5 caracteres críticos (< > & " '). Para la tipografía (signos de copyright, rayas, símbolos de moneda), usar el carácter literal UTF-8 en un documento UTF-8 es perfectamente correcto. Las entidades son más críticas en sistemas heredados o cuando no se puede garantizar la codificación de caracteres.

P: ¿Cuál es la diferencia entre &amp; y &?

& es el carácter ampersand literal. &amp; es su representación como entidad HTML. En el código fuente HTML, siempre que quieras mostrar un & literal, debes escribir &amp;. Si escribes un & solo antes de una palabra, los navegadores pueden intentar interpretarlo como el inicio de una entidad y renderizarlo incorrectamente.

P: ¿Por qué &nbsp; se comporta de forma diferente a un espacio regular?

Un espacio regular (U+0020) es un espacio que permite el salto de línea; los navegadores pueden romper líneas en él, y múltiples espacios consecutivos se colapsan en uno. &nbsp; (espacio de no ruptura, U+00A0) evita los saltos de línea entre los caracteres circundantes y no se colapsa. Es útil para mantener valores como "100 km" o "Dr. Smith" en una sola línea.

P: ¿Puedo usar entidades numéricas para emojis?

Sí. Los emojis tienen puntos de código Unicode y pueden representarse como entidades numéricas. El emoji 😀 (U+1F600) es &#x1F600; en hex o &#128512; en decimal. En documentos UTF-8 puedes pegar el emoji directamente, pero las entidades numéricas funcionan como un respaldo fiable.

P: ¿Cuál es el riesgo de XSS específico de los atributos href?

El atributo href tiene un peligro único: las URLs pueden usar el protocolo javascript:. La codificación HTML por sí sola no es suficiente:

<!-- PELIGROSO aunque < y > estén codificados: -->
<a href="javascript:alert(1)">Haz clic aquí</a>

<!-- Seguro: valida que el href comience con http:// o https:// -->
<?php
$url = $_GET['url'];
if (!preg_match('/^https?:\/\//i', $url)) {
    $url = '#'; // rechaza protocolos peligrosos
}
echo '<a href="' . htmlspecialchars($url) . '">Enlace</a>';
?>

P: ¿Es seguro usar innerHTML si codifico el contenido primero?

Si codificas correctamente los 5 caracteres críticos antes de asignarlos a innerHTML, generalmente es seguro para inyectar texto plano. Sin embargo, textContent es más simple e infalible. Reserva innerHTML para casos en los que intencionadamente quieras insertar una estructura HTML controlada.

P: ¿Los frameworks modernos de JavaScript manejan la codificación HTML automáticamente?

Sí: React, Vue, Angular y Svelte escapan la salida por defecto. El JSX de React escapa automáticamente los valores interpolados con {}. Sin embargo, cada uno proporciona un bypass explícito (dangerouslySetInnerHTML en React, v-html en Vue) que debe usarse con extremo cuidado y solo con contenido confiable.


Resumen

Las entidades HTML son un mecanismo esencial para:

  1. Seguridad: neutralizar <, >, &, ", y ' para prevenir la inyección de XSS.
  2. Corrección: asegurar que los caracteres con significado reservado en HTML se muestren literalmente.
  3. Compatibilidad: representar cualquier carácter Unicode en entornos heredados o restringidos.
  4. Tipografía: insertar rayas, espacios de no ruptura, símbolos de moneda y otros caracteres especiales de forma fiable.

En un stack UTF-8 moderno, principalmente necesitas codificar los 5 caracteres de seguridad críticos al enviar contenido dinámico a HTML. Las entidades con nombre como &nbsp; y &mdash; siguen siendo útiles para la tipografía. Entender la diferencia entre entidades con nombre y numéricas, y la divergencia entre las reglas de HTML y XML, te convertirá en un desarrollador web más eficaz y consciente de la seguridad.