HTML Escape / Unescape

Convierte <, >, &, comillas y apóstrofe a entidades y viceversa.

Descripción

Útil al incrustar fragmentos en HTML y plantillas. La decodificación usa el DOM del navegador.

Detalle técnico

Los cinco caracteres que fundamentan la seguridad HTML

  • `<` (menor que) → `&lt;`: Es el carácter que abre cualquier etiqueta HTML. Sin escape, cualquier contenido dinámico puede inyectar etiquetas arbitrarias. Debe ser siempre el primer carácter en escaparse en texto no confiable.
  • `>` (mayor que) → `&gt;`: Cierra etiquetas. Aunque los navegadores modernos toleran `>` sin escapar en algunos contextos, escaparlo sistemáticamente elimina una clase de vectores de inyección sutiles, como `</script>` dentro de bloques JavaScript inline.
  • `&` (ampersand) → `&amp;`: Inicia las entidades HTML. Debe escaparse antes que los demás: si escapas `<` antes que `&`, puedes terminar con `&lt;` siendo reescapado a `&amp;lt;`. El orden correcto es siempre: `&` primero.
  • `"` (comillas dobles) → `&quot;`: Crítico en atributos delimitados por comillas dobles. `<input value="{{userInput}}">` sin escape permite que el usuario introduzca `" onclick="alert(1)` y cierre el atributo prematuramente, inyectando manejadores de eventos.
  • `'` (comilla simple) → `&#39;`: Relevante en atributos delimitados por comillas simples. Menos común en HTML5 (las comillas dobles son el estándar), pero frecuente en plantillas PHP, Django y Rails que a veces usan comillas simples en la generación dinámica de atributos.

XSS persistente, reflejado y basado en DOM: tres vectores de ataque

  • XSS persistente (stored XSS): el payload malicioso se almacena en la base de datos y se muestra a todos los usuarios que visitan la página. Es el más peligroso: un solo post de foro o comentario de producto puede comprometer a todos los visitantes. El gusano Samy en MySpace fue stored XSS.
  • XSS reflejado: el payload viene de una URL o parámetro y se refleja inmediatamente en la respuesta. Ejemplo: `https://sitio.com/buscar?q=<script>robarCookies()</script>`. Requiere que la víctima haga clic en el enlace malicioso, pero es trivial distribuirlo via phishing o acortadores de URL.
  • DOM-based XSS: el propio JavaScript del sitio lee un valor de una fuente no confiable (`location.hash`, `document.cookie`, `window.name`) y lo escribe directamente en el DOM con `innerHTML` o `document.write` sin sanitizar. Nunca pasa por el servidor: las herramientas de análisis server-side no lo detectan.
  • Content Security Policy (CSP) es la segunda línea de defensa: un header HTTP que informa al navegador qué scripts están permitidos. `Content-Security-Policy: default-src 'self'` bloquea scripts de otros dominios y scripts inline sin nonce. No reemplaza el escape, pero mitiga el impacto del XSS residual.
  • XSS es diferente de CSRF (Cross-Site Request Forgery): XSS ejecuta código en el navegador de la víctima en el contexto del sitio objetivo; CSRF falsifica peticiones autenticadas sin ejecutar código. Ambos requieren defensas separadas: escape + CSP para XSS, tokens CSRF para CSRF.

Guía de la herramienta

  • Qué es HTML Marcado para páginas: etiquetas como <p>, atributos y entidades. Los caracteres especiales dentro de texto o atributos deben escaparse para no interpretarse como marcado.

  • Qué son las entidades HTML Representaciones como &lt; para <, &amp; para &, etc., para insertar texto literal sin romper el documento.

  • Qué hace la herramienta Convierte texto a entidades (escape) o decodifica entidades de vuelta a texto.

  • Por qué usarla Incrustar ejemplos en docs, plantillas, correo HTML o atributos value sin ejecutar etiquetas por error.

Fragmentos de Código

Función escapeHtml() en JavaScript (por qué & debe ser primero)
// MAL: escapar < antes de & puede crear doble escape
// Input: '<b>'
// Paso 1 (< → &lt;): '&lt;b>'
// Paso 2 (& → &amp;): '&amp;lt;b>' ← ¡doble escape!

// BIEN: & siempre primero
function escapeHtml(str) {
  return String(str)
    .replace(/&/g, '&amp;')   // 1. & PRIMERO
    .replace(/</g, '&lt;')    // 2. <
    .replace(/>/g, '&gt;')    // 3. >
    .replace(/"/g, '&quot;')  // 4. "
    .replace(/'/g, '&#39;');  // 5. '
}

// Ejemplos:
console.log(escapeHtml('<script>alert(1)</script>'));
// → &lt;script&gt;alert(1)&lt;/script&gt;

console.log(escapeHtml('5 < 10 & "dos" > \'uno\''));
// → 5 &lt; 10 &amp; &quot;dos&quot; &gt; &#39;uno&#39;
Sanitización con DOMPurify (configuraciones habituales)
import DOMPurify from 'dompurify';

// Básico: elimina todo lo peligroso, mantiene el formato
const htmlSeguro = DOMPurify.sanitize(htmlEntrada);

// Restrictivo: solo texto y formato básico
const soloTexto = DOMPurify.sanitize(htmlEntrada, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
  ALLOWED_ATTR: [],
});

// Con enlaces permitidos (pero javascript: bloqueado)
const conEnlaces = DOMPurify.sanitize(htmlEntrada, {
  ALLOWED_TAGS: ['a', 'p', 'strong', 'em', 'ul', 'ol', 'li'],
  ALLOWED_ATTR: ['href', 'title', 'target'],
  FORBID_ATTR: ['onerror', 'onload', 'onclick'],
});

// Verificar si el input fue modificado por la sanitización:
const original = '<p onclick="alert(1)">Texto</p>';
const limpio = DOMPurify.sanitize(original);
console.log(original === limpio); // false → el input contenía código malicioso

Antes / después

<script> → &lt;script&gt;

Preguntas frecuentes

¿Para qué sirve esta herramienta?

Funciona por completo en tu navegador: sirve para validar, formatear o convertir datos en el día a día.

¿Se envían mis datos a algún servidor?

El procesamiento es local con JavaScript. No almacenamos lo que pegas en los campos de texto.

¿Puedo usarlo con datos reales en producción?

Úsalo bajo tu responsabilidad. Para secretos (contraseñas, tokens), prefiere entornos controlados y políticas internas. Recuerda de revisar los contenidos generados. Nunca confies ciegamente en cosas que ves en internet.