Si compruebas cualquier Security Advisory actualizado, es fácil encontrar nuevas vulnerabilidades reflected y stored XSS descubiertas cada mes. Sin embargo no es igual para las DOM-Based XSS. Está completamente basada en el navegador y no necesita ir al servidor y volver. Antes, podía crearse una PoC de DOM-Based XSS con una página HTML estática. Sin embargo, con la evolución de los navegadores y su seguridad se ha dejado de poder y se ha vuelto todavía más difícil.
Antes de sumergirnos en las DOM-Based XSS, veamos lo que es un Document Object Model (DOM). El DOM es una interfaz de programación que representa un documento web como un árbol. Hace posible acceder y manipular las diferentes partes de la web de forma programada usando JavaScript.
Considera el HTML de example.com como era en Febrero de 2024 (abajo).

El árbol DOM sigue este formato:
document<!DOCTYPE html>htmltitlemetametametastyle
bodydivh1ppa
Podemos ver el DOM tree usando las herramientas de desarrollador del navegador.
Usando JavaScript podemos manipular el DOM tree. Por ejemplo, podemos crear un nuevo elemento usando document.createElement() y añadir un hijo a cualquier elemento usando element.append().
Aquí tienes un ejemplo de MDN Documentation.
let div = document.createElement("div"); let p = document.createElement("p"); div.append(p); console.log(div.childNodes); // NodeList [ <p> ]
En este ejemplo hemos creado dos elementos dic y p. Luego hemos añadido p a div.
Vulnerable Web Applications
Las vulnerabilidades DOM-Based toman lugar en el navegador. No necesitan ir al servidor y volver, es decir que el atacante inyectará código, por ejemplo, en la URL y este será ejecutado en el propio navegador.
Vulnerable "Static" Site
Consideremos el siguiente ejemplo. Es tan sencillo que es surrealista. Sin embargo, es suficiente para demostrar la DOM-Based XSS.
<!DOCTYPE html> <html> <head> <title>Vulnerable Page</title> </head> <body> <div id="greeting"></div> <script> const name = new URLSearchParams(window.location.search).get('name'); document.write("Hello, " + name); </script> </body> </html>
La página de arriba espera que el usuario le de su nombre en ?name=. En la imagen de abajo:
- El usuario ha introducido "Web Tester" después de
?nameen la URL. - El saludo funcionó como se esperaba, salió "Hello, Web Tester".
- Finalmente la estructura DOM en la derecha está intacta. El
<body>tiene 3 hijos.

El usuario puede intentar inyectar un script malicioso. En la imagen de abajo vemos:
- El usuario añadió
<script>alert("XSS")</script>en vez de sóloWeb Testercomo su nombre. - El script se ejecutó y la caja de alerta se desplegó.
- Más importante, podemos ver cómo el DOM tree ahora tiene un nuevo elemento bajo
<body>.

Este ejemplo básico muestra un par de cosas:
- El servidor no tiene rol directo en las vulnerabilidades basadas en DOM.
- El DOM fue modificado de forma insegura usando
document.write().
Fixed "Static" Site
<!DOCTYPE html> <html> <head> <title>Secure Page</title> </head> <body> <div id="greeting"></div> <script> const name = new URLSearchParams(window.location.search).get('name'); // Escape the user input to prevent XSS attacks const escapedName = encodeURIComponent(name); document.getElementById("greeting").textContent = "Hello, " + escapedName; </script> </body> </html>
Una manera de arreglar esta página es evitar añadir el input del usuario directamente con document.write(). En su lugar, primero lo escapamos con encodeURIComponent() y luego lo añadimos a textContent.
El intento anterior no funciona ahora.

