RSS 2.0 Atom 1.0 CDF  
JASoft.org - JavaScript
El blog de José Manuel Alarcón Aguín. Programación .NET y mucho más...
 

En mi anterior post hablé sobre el concepto de clausuras en Java_Script (JS a partir de ahora), y de cómo les podíamos sacar partido en programación avanzada en este lenguaje. En esta ocasión voy a rematar aquello comentando la existencia de auto-clausuras, un concepto muy interesante al que se le puede sacar partido para conseguir algo a priori imposible en este lenguaje: miembros privados en clases JS.

En JS podemos definir una clase de la manera convencional, tratando al mismo tiempo de encapsular el acceso a las variables privadas simulando la existencia de propiedades con funciones 'get' y 'set', por ejemplo así:

function coche() {
  var modelo = "NINGUNO";
  this.getModelo = function () {
    return modelo;
  };
  this.setModelo = function (sModelo) {
    modelo = sModelo.toUpperCase();
  };
}

var miCoche = new coche;
miCoche.setModelo("Ferrari");
alert(miCoche.getModelo());  //Muestra FERRARI

En este caso hemos definido una clase coche que tiene un miembro interno llamado modelo. Para intentar encapsular el acceso a este miembro y poder tener control sobre lo que se hace (que no sea una lectura o escritura directas, vamos) hemos definido dos funciones -que son clausuras- que harán las veces de los métodos getter y setter de una propiedad. De este modo para leer el valor del miembro "privado" usamos getModelo, y para escribirlo setModelo, que en este caso además se ocupa de que el nombre del modelo siempre vaya en letras mayúsculas (por poner un ejemplo de regla de negocio simple y no despistarnos del verdadero objetivo del ejemplo).

Así, si el programador usa estos dos métodos para leer y escribir los miembros privados, como en el ejemplo anterior, todo irá bien. Lo que ocurre es que nadie impide al programador ir directamente a la variable supuestamente privada y hacer esto:

miCoche.modelo = "Ferrari";
alert(miCoche.modelo);  //Muestra Ferrari en minúsculas

Con lo cual todo nuestro esfuerzo no vale absolutamente de nada.

Es posible hacer esto porque en realidad en las clases de JS no existen los miembros privados, ya que no hay modificadores de ámbito (public o private, por ejemplo) que nos permitan controlarlo.

¿Cómo podemos solucionar esto gracias a las clausuras?

Antes de continuar vamos a considerar un ejemplo de código muy sencillo como el siguiente:

  (function (){
    var interno = "Valor Interno";
    alert(interno);
    }
  )();

En este ejemplo hemos definido una función que se llama a si misma, y por lo tanto sólo existe durante el instante en que se ejecuta. No queda rastro de ella una vez que se ejecuta. Sí, es raro y aparentemente no tiene utilidad alguna, pero ten un poco de paciencia.

Lo que estamos definiendo en el fragmento anterior es una función anónima y también una auto-clausura. Es una función que se define, se ejecuta, y luego desaparece sin que pueda volver a ser usada. En realidad no desaparece sino que al no haber una referencia a la misma en ningún lugar de nuestro código no tenemos forma de usarla de nuevo ni de acceder a sus miembros. O sea, en cuanto se ejecuta la variable interna deja de ser accesible en nuestro código pues no hay una referencia a la función en ningún lugar.

¿Qué pasaría si de algún modo pudiésemos definir una función como esta que desaparezca pero quedarnos con una refencia a algo que esté en su interior (una clausura interna de esta función efímera)? Pues pasaría que, de repente, tendríamos acceso a los miembros de esta función efímera pero sólo de manera indirecta, nunca directa como en el caso anterior ya que no tenemos ninuna variable que apunte a la propia función.

Este es el concepto que usaron por primera vez los chicos de Yahoo y que ha permitido definir clases con miembros privados en JS.

Vamos a verlo con un ejemplo práctico reproduciendo la clase anterior que representa a un coche, pero esta vez que sólo sea accesible a través de los métodos getter y setter que hayamos definido:

var coche = function() { 
  var modelo= "NINGUNO";
  return {
    getModelo : function () {
      return modelo;
    },
    setModelo : function (sModelo) {
      modelo = sModelo.toUpperCase();
    }
  };
}();

alert(coche.modelo);  //Undefined
alert(coche.getModelo()); //NINGUNO
coche.setModelo("Smart Fortwo");
alert(coche.getModelo()); //SMART FORTWO

Fíjate bien en este código como se define una función que se ejecuta inmediatamente poniéndole un (); al final (línea 11), y almacena el resultado de su ejecución en una variable llamada coche. El código interno de esta función define una variable modelo que al igual que antes contendrá el nombre del modelo de coche que estamos manejando. Además como resultado de ejecutar la función se devuelve un nuevo objeto interno que tiene dos métodos que leen o fijan el valor de la variable interna. Como están dentro de la función son dos clausuras que tienen acceso a sus miembros (en este caso la variable modelo), pero como n queda referencia a la función, pues la estamos ejecutanod inmediatamente, entonces la única forma de acceso a la variable modelo que nos queda es a través de esas clausuras. ¡¡Lo que tenemos en la práctica es una variable privada!!. Algo imposible de conseguir a priori. Los alert del final del código anterior nos demuestran que así es. El primero de ellos intenta acceder a la variable modelo pero no puede, porque realmente tiene una refefencia a un objeto interno de la función, no a ésta, así que no puede y devuelve un valor "undefined" ya que no existe dicho miembro para el objeto coche. En las otras tres líneas usamos los métodos "legales" para acceder a la variable y todo funciona correctamente.

Instancias únicas de clase: Singleton

En la práctica ademas obtenemos una clase de instancia única (Singleton), ya que sólo existe una referencia a la misma y no es posible crear nuevas instancias. Esto es muy útil cuando estamos definiendo marcos de trabajo como el mencionado antes de Yahoo, y esta técnica ha sido usada desde entonces por otros frameworks como los mencionados en el post anterior.

Sin embargo nos impide crear clases con miembros privados y que podamos utilizar varias veces. Es decir, tal y como está el código anterior sólo podemos tener un único coche, no varios.

Si quitamos el doble paréntesis de antes (de la línea 11) que fuerza la ejecución, podemos trabajar con ella como si de una clase normal se tratara y seguimos teniendo acceso limitado a los miembros privados:

var Coche = function() { 
  var modelo= "Ninguno";
  return {
    getModelo : function () {
      return modelo;
    },
    setModelo : function (sModelo) {
      modelo = sModelo.toUpperCase();
    }
  };
};

var coche1= new Coche();
var coche2= new Coche();
alert(coche1.modelo);  //Undefined
coche1.setModelo("Porsche");
coche2.setModelo("Smart");
alert(coche1.getModelo()); //PORSCHE
alert(coche2.getModelo()); //SMART

Ahora creamos dos objetos de la clase Coche y les asignamos valores a través de su método setter. Si te fijas en la línea 15, intentamos acceder a su variable privada modelo sin éxito, pues se devuelve un indefinido, así que estamos consiguiendo en la práctica clases con miembros privados y que además podemos instanciar tantas veces como queramos.

Una vuelta de tuerca más al lenguaje de script más popular que tiene una gran utilidad práctica y que espero que te resulte útil :-)

Por: José Manuel Alarcon | Sunday, November 29, 2009 11:48:15 AM (Hora estándar romance, UTC+01:00)  #    Comments [2] - Trackback
Tags: JavaScript


Sígueme en:

:: Twitter JM Alarcón: tecnología, marketing, este blog y frikadas varias
:: Twitter campusMVP: los mejores recursos sobre tecnología Microsoft: trucos, artículos, noticias, vídeos...
:: Facebook campusMVP: los mismos mejores recursos pero en directamente en Facebook.
:: Boletín campusMVP Nuestra publicación electrónica, una vez al mes en tu buzón de correo.
 
Banner

JavaScript es un lenguaje de programación que, a pesar de los años que tiene, cada vez está más de moda y se utiliza más (y no sólo para la web). En los últimos años está viviendo una época dorada que pocos quizá le vaticinaron, gracias sin duda a la popularización de las técnicas AJAX tan necesarias para la Web 2.0.

Desde mediados de los '90 cualquier programador Web tenía que conocer como mínimo sus fundamentos (de ahí que mi libro sobre el tema vendiera en el año 2.000 un número indecente de ejemplares y fuera de los más vendidos de Amazon en su categoría en todos los idiomas). Sin embargo, lo cierto es que poca gente va más allá de su superficie, y la mayor parte de los programadores Web se quedan en esos fundamentos. Incluso muchos programadores lo rechazan y lo consideran un lenguaje de segunda categoría por ser debilmente tipado e interpretado. Esta misma semana un programador de mi empresa, recién salido de la Universidad, me dijo que en la carrera les desaconsejaban utilizarlo. ¡Lo que es el desconocimiento!

JavaScript es un lenguaje potentísimo que permite hacer verdaderas maravillas bien utilizado. Hay bibliotecas como jQuery, YUIDojo, Microsoft Ajax Library, etc... que son una virguería. Y el lenguaje en sí tiene algunas características únicas que ya quisieran para sí otros lenguajes que a priori son más "profesionales".

Una de estas características es de la que voy a hablar en este post. Se trata de las Closures o Clausuras, provinientes de la programación funcional y muy útiles en algunas ocasiones. Están presentes también en otros lenguajes como C#, Groovy o Lisp. Es muy probable que incluso las hayas utilizado en algún desarrollo de JavaScript sin haberte percatado. Tienen muchas aplicaciones en el actual desarrollo orientado a objetos con JavaScript, por lo que todo programador de JavaScript debería conocerlas.

El ámbito de las variables en JavaScript

Lo habitual cuando trabajamos con JavaScript es fijarnos en que hay dos tipos de ámbitos para las variables: Global y Local. Por ejemplo, en el siguiente fragmento de código JavaScript:

var glo = 1;

function PruebaLocales() {
  var loc = 2:
  alert(glo);  //Muestra un 1
  alert(loc);  //Muestra un 2
}

alert(glo);  //Muestra un 1
alert(loc); //Fallará

La variable global 'glo' es accesible desde cualquier parte de nuestro script, ya que se a definido fuera de cualquier otro ámbito, de manera independiente. Sin embargo la variable local 'loc' se ha definido dentro de una función, y por lo tanto sólo tiene validez dentro de ésta, y por lo tanto la última instrucción fallará, puesto que estamos intentando llamarla desde fuera de la función.

Como resumen se podría decir que nuestro código puede acceder a variables que tengan un ámbito mayor que en el que se encuentran pero no menor. Así, desde una función sólo podremos acceder a las variables propias de dicha función, que no son accesibles desde otros ámbitos.

La verdadera regla de acceso a variables

Ahora vamos a considerar el siguiente código pefectamente válido en JavaScript:

function pruebaClosure() {
  var loc = "¡Hola closure!";
  this.muestraMensaje = function() {
      alert(loc);
    };
}

var obj = new pruebaClosure();
obj.muestraMensaje();  //Muestra el mensaje de saludo

En este fragmento hemos definido una función/clase que dispone de una función interna definida como método de la clase. Es similar a tener definida una función dentro de otra función (anidada). Lo curioso en este código es que funciona perfectamente y muestra el mensaje contenido en la variable local. Es decir, que en este caso la regla de ámbito parece que se rompe ya que tenemos una función que es capaz de acceder a una variable que no pertenece a la propia función.

Sin embargo no es así y la regla sigue siendo vigente. La verdadera regla de acceso a variables es que una línea de nuestro código sólo puede acceder a variables que tengan un ámbito mayor o igual que el suyo propio. Y en este caso se está cumpliendo ya que la variable 'loc' tiene un ámbito mayor que el de la línea de código que lanza la alerta, que tiene un ámbito (el de su función) todavía más restringido.

Piénsalo como una jerarquía donde los niveles inferiores siempre tienen acceso a los superiores. Si estuviésemos hablando de un país la cosa sería que los ciudadanos de una ciudad tienen acceso a los servicios públicos de la ciudad, la región y el país, pero en niveles superiores no es así, de modo que un habitante de la región pero no de la ciudad que estamos considerando tiene acceso a los servicios de la región y del pais, pero no a los de la ciudad por no ser un ciudadano de la misma. Espero haberme explicado :-)

El verdadero aspecto de las Closures

Las clausuras se llaman así porque en realidad lo que representan es a funciones que tienen acceso a una serie de variables, libres de su propio ámbito (están en un ámbito superior), y que hacen uso de ellas mediante una sintaxis externa que es la que las establece y le da el sentido (las cierra o clausura, de ahí el nombre).

Lo explicaré mejor con un ejemplo más apropiado como el siguiente código JavaScript:

function concatenar(s1) {
      return function(s2) {
        return s1 + ' ' + s2;
      };
}

var diHola = concatenar("Hola");
alert( diHola("visitante") );

Vaya. Sí que es enrevesado ¿no?. Lo que muestra en pantalla es un mensaje como este:

Es decir, en la práctica gracias al uso de una clausura hemos definido una función que permite asignar valores para ejecución retardada. Esto es algo realmente útil, y no es la única aplicación práctica de las clausuras, pero gracias a ello podemos habilitar todo un sistema de ejecución en diferido de funciones JavaScript. Esto nos permite crear punteros a funciones pre-parametrizadas y con ello crear un sistema de eventos, lanzar acciones periódicas complejas y particularizadas con setInterval o SetTimeout... y muchas otras cosas.

Ejemplo de ejecución diferida

Por ejemplo, consideremos un ejemplo sencillo pero revelador de cómo funcionan estas técnicas que he mencionado. Se trata de lanzar una función con parámetros cada cierto tiempo, con setInterval o con setTimeOut. Esta dos funciones toman como parámetros una referencia a la función a ejecutar y el tiempo en milisegundos al cabo del cual debe ejecutarse (periódicamente o sólo una vez, según sea uno u otro método). Pero tienen una limitación: no permiten pasar parámetros a la función a ejecutar. Así que no sirve para llamar a funciones que tomen parámetros para particularizar su funcionamiento.

La solución chapucera sería definir una función sin parámetros que a su veces llame a la que queremos llamar nosotros con los parámetros requeridos. Una solución mucho más elegante es usar una clausura, como en este ejemplo:

function moverElemento(elto, x, y)
{
  return function(){
    elto.style = "position:absolute; x:"+ x + ";y:" + "y;";
  };
}
var mover = moverElemento(getElementById("miDiv"), 0, 0);
setTimeout(mover, 500);

En este caso he utilizado un ejemplo sencillo (la sintaxis de posicionamiento puede que ni esté bien) pero lo que quería ilustrar es cómo usando el artificio de las closures es posible definir punteros a funciones JavaScript parametrizadas en su comportamiento.

Piénsalo y verás las amplias posibilidades que abre. Una de ellas, que contaré en un próximo post es la posibilidad, a priori imposible, de definir variables privadas en objetos JavaScritp, protegiendo el acceso a las mismas de forma que los programadores irresponsables no pudan tocarlas y tengan que ir a través de los cauces que les marquemos.

Las closures abren todo un mundo de posibilidades para la programación JavaScript.

¡Espero que te resulte interesante!

Por: José Manuel Alarcon | Sunday, November 22, 2009 12:38:11 PM (Hora estándar romance, UTC+01:00)  #    Comments [0] - Trackback
Tags: JavaScript


Sígueme en:

:: Twitter JM Alarcón: tecnología, marketing, este blog y frikadas varias
:: Twitter campusMVP: los mejores recursos sobre tecnología Microsoft: trucos, artículos, noticias, vídeos...
:: Facebook campusMVP: los mismos mejores recursos pero en directamente en Facebook.
:: Boletín campusMVP Nuestra publicación electrónica, una vez al mes en tu buzón de correo.
 
Banner

Los temporizadores (Timer) de las extensiones de AJAX para ASP.NET son muy útiles. Nos permiten ejecutar una determinada tarea cada cierto tiempo, provocando postbacks de la página  (tanto completos, como parciales) en intervalos regulares. Un único Timer colocado en la página puede conseguir que se refresquen todos los UpdatePanels disponibles o cada uno de manera individual.

Lo habitual es colocarlos en la página y olvidarnos de ellos. Pero ¿qué pasa si queremos poder pararlos y activarlos a voluntad?

La cosa tiene más complicación de la que parece a simple vista. Lo primero que se nos ocurre a cualquiera es que, dado que tiene una propiedad Enabled para activarlo y desactivarlo bastará con establecerla en False para conseguir el efecto deseado. Si lo hacemos desde un postback asíncrono enviado desde dentro de un UpdatePanel, ni se notará en la página, ¿no?.

Lo malo de esta idea es que, simplemente, no funciona. El motivo es que en un refresco parcial de página, aunque se establezca la propiedad del temporizador, al retornar desde el servidor el cambio no se ve reflejado en el código JavaScript que controla el funcionamiento del mismo (un temporizador no es más que un simple setInterval() de JavaScript de los de toda la vida que provoca por código un refresco parcial de la página).

Entonces, para conseguirlo no nos queda más remedio que controlarlo desde el lado cliente, con JavaScript, lo cual tiene la ventaja añadida de que no es necesario un viaje al servidor para una cosa tan prosaica como esta, así que mejor.

Si observamos el código fuente del control Timer en lado cliente (basta con pulsar F12 en Internet Explorer y ver el código desde la pestaña Script) verás que tiene las siguientes definiciones en su prototipo:

Sys.UI._Timer.prototype = {
    get_enabled: Sys$UI$_Timer$get_enabled,
    set_enabled: Sys$UI$_Timer$set_enabled,
    get_interval: Sys$UI$_Timer$get_interval,
    set_interval: Sys$UI$_Timer$set_interval,
    get_uniqueID: Sys$UI$_Timer$get_uniqueID,
    set_uniqueID: Sys$UI$_Timer$set_uniqueID,
    dispose: Sys$UI$_Timer$dispose,
    _doPostback: Sys$UI$_Timer$_doPostback,
    _handleEndRequest: Sys$UI$_Timer$_handleEndRequest,
    initialize: Sys$UI$_Timer$initialize,
    _raiseTick: Sys$UI$_Timer$_raiseTick,
    _startTimer: Sys$UI$_Timer$_startTimer,
    _stopTimer: Sys$UI$_Timer$_stopTimer,
    _update: Sys$UI$_Timer$_update
}

Como podemos ver es posible hacer muchas cosas desde lado cliente, en concreto las interesantes son: ver/establecer si está habilitado o no, ver/establecer su intervalo, pararlo y ponerlo de nuevo en marcha.

Sabiendo esto tomar control sobre el timer es muy sencillo, basta con incluir este código JavaScript:

<script language="javascript" type="text/javascript">

var tmr = null;

   function ReferenciaTimer() { 
      if (tmr == null)
         tmr = $find("<%= Timer1.ClientID %>");
      return tmr;
   }

   function PararTimer() {
      ReferenciaTimer()._stopTimer();
   }

   function LanzarTimer() {
      ReferenciaTimer()._startTimer();
   }
</script>

Lo único que hago es obtener una referencia al control Timer usando su identificador de cliente (que se "inyecta" desde el servidor al generar la página gracias a una expresión <%= %> de ASP.NET) y la función especial de AJAX $find que sirve para obtener referencias a los componentes en el lado cliente. Luego para pararlo o ponerlo en marcha llamo los método _stopTimer o _startTimer del control JavaScript, que hemos visto en su propotipo. Si colocamos dos botones HTML normales y corrientes que llamen a estas dos funciones que acabo de definir:

   <input id="Button1" type="button" value="Parar Timer" onclick="PararTimer();" />
   <input id="Button2" type="button" value="Lanzar Timer" onclick="LanzarTimer();" />

¡Listo!

Con esto tenemos controlado el Timer y sin necesidad de ir al servidor para nada. Por supuesto podemo susar un código similar para cambiar o leer su intervalo de refresco o, incluso, para provocar manualmente un evento "tick" que lance el postback asociado al control.

He preparado un pequeño ejemplo práctico con todo lo explicado que puedes descargar desde aquí (3,11 KB)

¡Espero que te sea útil!

Por: José Manuel Alarcon | Monday, November 02, 2009 8:53:18 PM (Hora estándar romance, UTC+01:00)  #    Comments [0] - Trackback
Tags: AJAX | ASP.NET | JavaScript


Sígueme en:

:: Twitter JM Alarcón: tecnología, marketing, este blog y frikadas varias
:: Twitter campusMVP: los mejores recursos sobre tecnología Microsoft: trucos, artículos, noticias, vídeos...
:: Facebook campusMVP: los mismos mejores recursos pero en directamente en Facebook.
:: Boletín campusMVP Nuestra publicación electrónica, una vez al mes en tu buzón de correo.
 
Banner

En mis tres anteriores post me centré en JSONP, la solución soportada actualmente por cualquier navegador para hacer llamadas a servicios JSON ubicados en dominios diferentes al actual (ver índice al final de este post). Ahora, tras quince dís sin tiempo para nada (con Codecamp y TTT para Microsoft por el medio), por fin saco un par de horitas para escribir esta últma parte de la serie, dedicada a los estándares.

Las limitaciones del objeto XmlHttpRequest a la hora de hacer peticiones desde el navegador a páginas y recursos ubicados en otros dominios son de sobra conocidas. Ello ha hecho que los programadores busquen atajos para conseguir esta funcionalidad y saltarse esta barrera. Hasta ahora hemos visto cómo JSONP nos permite conseguirlo gracias al uso de la etiqueta <script>, y cómo se le puede sacar partido en muchas ocasiones. No obstante esta técnica tiene bastantes limitaciones:

· Sólo sirve para peticiones GET
· Los datos deben devolverse en formato JSON, o sea, JavaScript.
· La información que se puede enviar es muy limitada (únicamente datos planos como parámetros de la URL)
· El servidor debe colaborar explícitamente con el método para devolver el código envuelto en una llamada a un método JavaScript
· No existe un método estándar para controlar el acceso a este tipo de recursos remotos por parte del servidor que los provee, es decir, decidir quién y cuándo puede acceder al recurso.

Conscientes de esta necesidad en el World Wide Web Consortium (W3C), el organismo encargado de regular los estándares para la Web, han estado trabajando en una modificación del modo de funcionamiento habitual del objeto XmlHttpRequest para dotarlo de capacidades de acceso a otros dominios, así como de las convenciones necesarias para que los servidores que ofrecen servicios puedan regular el acceso de los navegadores a los mismos. El último borrador en el momento de escribir esto es de marzo de este año, lo escribe una persona que trabaja para el navegador Opera, y se puede leer aquí: Cross-Origin Resource Sharing. A partir de ahora en este texto le llamaré CORS a este protocolo, para facilitar la lectura.

Funcionamiento básico de CORS

El comportamiento por defecto del objeto XmlHttpRequest impide que se puedan hacer llamadas a recursos en otros dominios, impidendo así la realización de ataques de XSS y de XSRF, como ya hemos visto en los artículos anteriores. Lo que pretende la W3C con CORS es definir un protocolo para que los servidores Web y los navegadores colaboren a la hora de definir políticas de seguridad de acceso entre dominios.

Así, en su forma más básica, CORS define que cuando se haga una llamada con XmlHttpRequest a un dominio diferente del actual, dicha llamada debe incluir automáticamente una cabecera llamada "Origin" que indicará al servidor de destino el protocolo, dominio y puerto desde el que se está originando la petición. Esta cabecera difiere de la cabecera "Referer" en que no se indica ningún tipo de ruta, por lo que las preocupaciones de privacidad de los posibles usuarios son menores.

Con esta cabecera el servidor debe decidir si permite o no el acceso al recurso, devolviendo en su respuesta una cabecera de tipo Access-Control-Allow-Origin en la que indica al navegador qué dominios son permitidos como origen de las peticiones. Por ejemplo, puede devolver:

Access-Control-Allow-Origin: http://www.tuservidor.com

o, en muchos casos:

Access-Control-Allow-Origin: *

que significa que admite peticiones desde cualquier dominio externo.

Al recibir esta cabecera el navegador sabe si debe permitir o no que se realice la llamada. De esta forma se protege a los usuarios de un uso indebido de JavaScript para traspasar u obtener información entre dominios debido a alguna vulnerabilidad en el código de la página que permita inyectar JavaScript para hacer XSS o XSRF.

Por defecto, si el servidor no incluye esta cabecera el navegador bloquea la llamada, con lo que el comportamiento es exactamente el mismo que el tradicional.

Lo mejor es que realmente los programadores del lado cliente (del JavaScript del navegador) no tienen que hacer nada especial para soportar estas llamadas entre dominios, ya que pueden usar el objeto XmlHttpRequest como ya están acostumbrados a hacerlo y sobre lo que ya he hablado en este blog anteriormente, sólo que en la URL de destino incluyen la ruta completa, con el nombre de otro dominio.

El protocolo define todavía más cabeceras para cuestiones algo más especializadas, como por ejemplo para cachear el resultado de una valoración de seguridad y cuánto tiempo permanece guardada. Otra cosa muy interesante es lo que el borrador de CORS denomina "preflight requests". Se trata de una cómo provocar una petición previa por parte del navegador que, antes de solicitar el verdadero recurso en el que el código está interesado, lanza una petición con el método HTTP OPTIONS en el que el servidor le devuelve las condiciones de seguridad de las llamadas desde ese origen. El navegador las cachea y reutiliza durante un tiempo determinado. El programador no tiene que hacer dos llamadas para consegui esto, sino que con una única llamada mediante XmlHttpRequest, construida de determinada manera, provoca la obtención de esta información. Si tienes interés en profundizar en el estos detalles puedes leerte el borrador de CORS o también el documento que tiene al respecto la gente de la fundación Mozilla (que merendilla :-P).

Pero ¿qué conseguimos con esto?

Puede que a estas alturas te estés diciendo: "Vale, estupendo, el servidor indica qué dominios tienen acceso pero ¿cómo impides que con esto alguien haga una llamada al servicio con cualquier otro método manual, sin pasar por el navegador?¿Qué modo de proteger mi aplicación del servidor es esta?".

La respuesta es que este protocolo no pretende proteger el acceso a tus aplicaciones de servidor. No es un método de seguridad para servicios en Internet. Lo que busca es proteger a los usuarios que navegan usando un navegador estándar de problemas en el código de las páginas que visitan, de modo que de forma inadvertida para ellos un pirata pueda usar técnicas de inyección de código para obtener acceso a su información. Revisa el tercer post de esta serie, más abajo, para recordar cómo son este tipo de ataques.

Esta es una distinción fundamental, ya que muchos programadores a los que se les habla de este protocolo piensan que les servirá para proteger sus recursos en el servidor, y es justo al contrario: es para proteger el uso indebido de código de cliente. Así que ojo con esto. La protección de los recursos del servidor no tiene nada que ver con esto y cada aplicación deberá usar el método, estándar o no, que considere oportuno (como el uso de Cookies cifradas, como ya he analizado en este blog).

Por otra parte el hecho de que podamos usar XmlHttpRequest para este tipo de llamadas nos quita de un plumazo todas las limitaciones que comentaba al principio del post, ya que podemos enviar peticiones GET y POST, cuaqluier tipo de información de cualquier longitud y en cualquier formato de texto (XML, JSON...), y tampoco necesitamos colaboración explícita por parte del servidor más que la inclusión de la cabecera, algo que no complica la programación en absoluto.

Así que si expones un servicio en cualquier formato y quieres darle un punto más de seguridad a tus usuarios incluye la cabecera Access-Control-Allow-Origin para indicar desde qué dominios podrá ser utilizado.

Soporte de navegadores

En la actualidad, y a pesar de que se trata todavía de un borrador, la especificación de este protocolo está soportada por todos los navegadores recientes, es decir, por Firefox 3.5, Safari 4 y Chrome 2.0.

"¿Eh? Un momento, ¿no acabas de decir todos los navegadores recientes?. ¿Y donde está Internet Explorer 8.0? ¿Y Opera?"

Curiosamente, aunque alguien de Opera es el responsable del borrador en la W3C, el fabricante europeo parece que no se ha decidido a implementarlo todavía.

Por otro lado, lamentablemente, Microsoft ha ido por su cuenta con este asunto, ofreciendo su propia implementación y permitiendo hacer lo mismo mediante un nuevo objeto especial llamado XDomainRequest. En realidad, como verás si te lees el enlace anterior, el uso es casi igual al que haríamos con el objeto XmlHttpRequest, pero tiene pequeñas diferencias. Pero por lo demás funciona prácticamente igual y acepta las mismas cabeceras desde el servidor.

Esto, como puedes imaginar, ha creado una nueva corriente anti-IE entre la comunidad más activa en el desarrollo Web y los estándares. Mi opinión personal es también la misma: que deberían haber seguido el borrador al pie de la letra, con el objeto XmlHttpRequest y no con un objeto nuevo que le complique la vida a los scripts multi-navegador. Y más si pensamos que con IE8 Microsoft ha dado un giro espectacular hacia el soporte estricto de los estándares Web, alejándose de la nefasta corriente con la que tontearon con IE6 (motivo principal del clamor popular para eliminar esta infame versión 6.0 del navegador de Microsoft)

¿Por qué decidió Microsoft crear un objeto propio y no hacer como los demás lo que indica el borrador del estándar?. El motivo oficial es que consideran que CORS en su forma actual no es totalmente seguro. En un post en el blog del equipo de Internet Explorer hacen referencia a un documento sobre seguridad en el que indican (en la sección 5) entre otras los posibles ataques que se pueden producir con este protocolo. Gran parte de la comunidad de desarrolladores no está de acuerdo y han contestado a estas reservas que tiene el equipo de IE, como se hace eco este post de ajaxian, blog muy activo dedicado al mundo del desarrollo de lado cliente.

En resumen

CORS es una gran iniciativa para librarnos a los programadores de las dificultades actuales para hacer llamdas entre dominios y, al mismo tiempo, conseguir seguridad para los usuarios. Los principales navegadores en sus últimas versiones soportan el último borrador del protocolo, con la notable excepción de IE8, y tampoco Opera, aunque en este caso dado su baja cuota de usuarios no supone demasiado problema.

Conviene que tratemos de usarlo en nuestros desarrollos de servicios para mejorar la seguridad de los usuarios y aunque no esté totalmente soportado aún.

Es de esperar que cuando el protocolo pase de la fase de borrador a versión definitiva todos los navegadores lo soporten (esperemos que también IE).

Con esto termino la serie de artículos sobre llamadas a servicios entre dominios. Espero que te haya resultado útil, y recuerda que si quieres formarte en desarrollo Web o en cualquier tecnología de Microsoft, en campusMVP encontrarás los mejores cursos on-line tutelados por conocidos MVP.

Las otras partes de esta serie:

· Parte 1: JSONP: llamadas AJAX a servidores remotos
· Parte 2: JSONP (II): Soporte desde ASP.NET AJAX 4.0
· Parte 3: JSONP (yIII): Cuestiones de seguridad y ASP.NET rompiendo la compatibilidad en 3.5

Por: José Manuel Alarcon | Saturday, October 24, 2009 3:01:44 PM (Hora de verano romance, UTC+02:00)  #    Comments [0] - Trackback
Tags: AJAX | ASP.NET | JavaScript


Sígueme en:

:: Twitter JM Alarcón: tecnología, marketing, este blog y frikadas varias
:: Twitter campusMVP: los mejores recursos sobre tecnología Microsoft: trucos, artículos, noticias, vídeos...
:: Facebook campusMVP: los mismos mejores recursos pero en directamente en Facebook.
:: Boletín campusMVP Nuestra publicación electrónica, una vez al mes en tu buzón de correo.
 
Banner

En mis dos anteriores post (busca los enlaces al final de este post y leetelos antes si no lo has hecho) he estado hablando sobre la técnica de JSONP para consumo remoto de datos JSON entre dominios, algo a priori prohibido por el modelo de seguridad del navegador. Anteriormente comentaba que JSONP podría tener ciertos problemas de seguridad, y que para evitarlos en ASP.NET 3.5 se habían introducido cambios para paliarlos que rompían la compatibilidad con versiones anteriores. Voy a aclararlo ahora.

El problema de seguridad tiene que ver con la obtención de acceso no autorizado a información privada que se comparte con JSON.

Una de las técnicas habituales de securizar el acceso a los servicios, consiste en crear cookies encriptadas con una validez temporal limitada y asociadas a una sesión concreta. Esta técnica se utiliza de manera mayoritaria en los sitios Web actuales (como Facebook, Windows Live, o, como ya hemos visto, en la propia seguridad de ASP.NET). El problema de esta técnica es que la mayor parte de los navegadores actuales conservan las sesiones entre las diferentes pestañas abiertas (e incluso entre las diferentes ventanas), lo cual puede ser aprovechado por los atacantes para realizar ataques de phising y acceder a esa información privada en ciertos casos. Esto de las sesiones no tiene remedio fácil en navegadores como Firefox (al menos que yo sepa), pero en Internet Explorer 8.0 sí, como ya he explicado en su día.

Bien, supongamos que tenemos un servicio Web que devuelve información privada en formato JSON y que comprueba el acceso del usuario actual basándose en una cookie encriptada. El usuario tiene abierto el navegador con nuestra aplicación y recibe un correo electrónico de un atacante que lo "fuerza" a pulsar en un enlace aparentemente legal, pero que lo lleva a otra página controlada por el atacante. En esta página el pirata intenta una llamada a nuestro servicio JSON usando técnicas de JSONP, es decir, empleando etiquetas de tipo <script> para hacer la llamada. Al hacerlo, como las sesiones se conservan entre pestañas y ventanas, el servicio "se fía" y envía la información.

Lo que ocurre es que esta información generalmente será una matriz de datos JSON del estilo de la que vimos en el anterior post que devolvía Delicio.us. Al recibir en la página esta matriz, dentro de un <script>, aunque es válida realmente el atacante no puede hacer nada porque se recibe y al ser pura información, no asignada enningún sitio, simplemente se recibe y se descarta ¿no?

El problema es que en JavaScript es perfectamente lícito redefinir el constructor de una matriz, de modo que cuando se cree una de ellas se lleven a cabo algunas operaciones adicionales, por lo que el pirata puede escribir un código tan sencillo como este:

<script type="text/javascript">

var datos;

Array = function() {
  datos = this;
};

</script>

Lo que se hace es redefinir el constructor de las matrices en JavaScript, de modo que se asigna a una variable el contenido de ésta al ser creada. Este sencillo truco hace que al recibir el JSON desde el otro dominio, los datos se encuentren listos para ser usados por el pirata en el momento que lo desee, sin necesidad de estar de acuerdo con el otro servidor para envolver el JSON en una llamada a una función JavaScript, que sería lo normal y es lo que tendríamos que hacer normalmente. El pirata podría enviar a un tercer dominio los datos o hacer con ellos lo que quisiera.

El impacto de estas técnicas

¿Te parece un ataque muy rebuscado? Quizá lo sea, pero debes saber que GMail fue crackeado hace no tanto tiempo con una técnica similar para obtener acceso a los contactos de cualquier usuario. Además es, en definitiva, una técnica habitual de Cross Site Request Forgery (CSRF), las cuales tienen bastantes casos de éxito en todo tipo de Webs.

No obstante sí que hay algo que minimiza el posible impacto de esta técnica en la actualidad, y es que las últimas versiones de los navegadores están protegidas "de serie" contra ello. Así, ni Internet Explorer 7 u 8, ni Firefox 3 ni Chrome o Safari son vulnerables a la redifinición del constructor de la matriz o de la clase genérica Object. Firefox 2.x síque lo es, por ejemplo.

¿Quiere decir esto que estmao slibres de todo peligro?. En absoluto. para empezar tus usuarios pueden estar usando navegadores viejos. Además, lo cierto es que tus datos son accesibles desde otros dominios sin que esa fuera tu intención inicial, y hay otras posibles técnicas que se pueden utilizar (muchas de las cuales ni siquiera son conocidas hoy, pero pueden surgir). Por ejemplo, Twitter fue crackeado no hace mucho usando técnicas avanzadas de JavaScript y el acceso a los datos JSON remotos usando técnicas JSONP. Y otras similares pueden surgir en cualquier momento.

¿Qué se puede hacer para evitar este problema y otros del estilo que pueden aparecer?

Hay muchas recomendaciones que podemos seguir para evitar este problema.

· Primeramente podemos exigir que el acceso a nuestro servicio privado se haga siempre mediante POST y no usando el más habitual GET. El motivo es que, obviamente, la etiqueta <script> de HTML hace siempre la llamada usando un método GET, y por lo tanto este tipo de ataque no funcionaría.
· Otra técnica sencillña y efectiva es comprobar las cabeceras de la petición. Cuando la petición se hace con el objeto XmlHttpRequest generalmente las diferentes bibliotecas AJAX marcan la petición con una cabecera como esta: "X-Requested-With: XMLHttpRequest", para indicar que es una petición AJAX, y distinguirla de una normal. jQuery y ASP.NET AJAX lo hacen. Y si lo hacemos nosotros mismos es muy fácil incluirla también. Además generalmente si se hace una petición que espera JSON se suele indicar también el tipo de contenido esperado con una cabecera "Content-Type: application/json", cosa que tampoco hace la etiqueta <script>.

Otra cosa que hicieron los programadores de .NET para evitar el ataque descrito es incluir el famoso parámetro "d:" en todas las respuestas JSON de los servicios Web, tanto .asmx como WCF, expuesto como JSON.

En AJAX 1.0 para ASP.NET 2.0, cuando se devolvía un dato o colección de datos como JSON a través de un servicio Web, se obtenía un dato JSON normal y corriente, como el que acabamos de ver para Delicio.us, por ejemplo:

{"__type:": "Usuario", "Login":"usuario1", "Clave": "miclave"}

Sin embargo en la versión 3.5 de .NET (es decir, en realidad la versión de AJAX 1.0 específica para ser usada con Visual Studio 2008) y posteriores (ASP.NET 4.0), todas las respuestas JSON de servicios incluyen un parámetro adicional de nombre "d:", lo que convierte las respuestas en algo como esto:

{"d": {"__type:": "Usuario", "Login":"usuario1", "Clave": "miclave"} }

La diferencia en este caso es que siempre nos aseguramos de que lo que se devuelve es un objeto con un miembro de nombre "d" que es el que contiene los datos. En este ejemplo no se ve tanto la ventaja porque ya se devolvía un objeto, pero si devolviésemos una matriz de objetos (algo muy habitual) o un dato simple (como una cadena o un número) la cosa sí está más clara, ya que al devolverlo con forma de objeto y este miembro "d", conseguimos:

1.- Evitar que, aunque se redefina el constructor de la matriz, ello no tenga efecto sobre el acceso al JSON devuelto.
2.- El código no se pueda evaluar directamente en destino.

No tiene porqué ser una medida infalible porque pueden aparecer nuevos tipos de ataques que se salten la seguridad también, pero ayuda. Además, sigue siendo recomendable comprobar las cabeceras o permitir sólo POST para las peticiones de ciertos métodos de servicios, como ya he comentado.

Incompatibilidad

La principal consecuencia de añadir la "d" en las repsuestas JSON es que si tenemos código manual para leer estas respuestas y procesarlas, si cambiamos de versión de .NET (de la 2.0 a una superior) nos dejará de funcionar el código de JavaScript, debiendo, en tal caso hacer una de dos cosas:

1.- Leer las verdaderas propiedades de nuestros datos desde la propiedad "d" del objeto devuelto.
2.- Procesar previamente la respuesta antes de evaluarla para quitarle esa "d"

Si usamos ASP.NET AJAX no tendremos que preocuparnos pues ya hace esto último por nosotros,pero si el código es manual o usamos otras bibliotecas como jQuery, entonces tendremos que tenerlo en cuenta.

La mejor forma de verificarlo si tenemos que hacerlo de forma manual es usando la propiedad hasOwnProperty de la clase Object de JavaScript, soportado en todos los navegadores modernos (incluso IE6 y Firefox 2.0 lo soportan). Por ejemplo, si tenemos un servicio propio con las medidas de seguridad propuestas (sólo admite POST y comprueba que el tipo de datos solicitado sea JSON), si queremos llamarlo desde jQuery, con su función Ajax para llamadas a servicios, podríamos hacer esto y que funcionara con cualquier versión de .NET:

$.ajax({
  type: "POST",
  url: "miservicio.asmx/mimetodo",
  data: "{}",
  contentType: "application/json; charset=utf-8",
  dataType:"json",
  success: function(datos) {
    if (datos.hasOwnProperty("d"))
      ProcesarResultado(datos.d);
    else
      ProcesarResultado(datos);
  }
});
 
function ProcesarResultado(datos) {
  // aquí procesamos lo devuelto con la confianza de que nunca tendrá la "d"
}

Lo único que se hace es pre-procesar el resultado comprobando si tiene la propiedad "d" característica de ASP.NET 3.5+ o no, y si la tiene usar la propiedad para obtener los datos necesarios, o usar directamente lo devuelto en caso contrario.

Con cualquier otra biblioteca de AJAX o con código propio haríamos lo mismo o muy similar.

En resumen

La seguridad de los servicios basados en JSON puede estar comprometida entre dominios si no tenemos un poco de cuidado. ASP.NET AJAX a partir de .NET 3.5 incluye algunas medidas de protección, las cuales pueden producir algunas incompatibilidades. Si tenemos un poco de cuidado es fácil proteger a los usuarios de un uso indebido de su inforamción privada.

En un próximo post quiero comentar qué está preparando la W3C en las próximas versiones de HTML para facilitar nativamente las llamadas entre dominios y cómo incluso algunos navegadores hoy en día soportan estas características más seguras y estándar.

Las otras partes de este post:

· Parte 1: JSONP: llamadas AJAX a servidores remotos
· Parte 2: JSONP (II): Soporte desde ASP.NET AJAX 4.0

Por: José Manuel Alarcon | Monday, October 12, 2009 1:13:23 PM (Hora de verano romance, UTC+02:00)  #    Comments [0] - Trackback
Tags: AJAX | ASP.NET | JavaScript


Sígueme en:

:: Twitter JM Alarcón: tecnología, marketing, este blog y frikadas varias
:: Twitter campusMVP: los mejores recursos sobre tecnología Microsoft: trucos, artículos, noticias, vídeos...
:: Facebook campusMVP: los mismos mejores recursos pero en directamente en Facebook.
:: Boletín campusMVP Nuestra publicación electrónica, una vez al mes en tu buzón de correo.
 
Banner
Page 1 of 4 in the JavaScript category Next Page
Copyright © 2010 José Manuel Alarcón Aguín. All rights reserved.