viernes, 31 de mayo de 2013

Seguridad en aplicación web con Apache Tapestry (II)

Apache Tapestry
Si en la anterior entrada hablaba de la seguridad sobre los aspectos de la autenticación y autorización de las acciones que puede realizar los usuarios en una aplicación web en esta hablaré como protegerse de otro tipo de fallos de seguridad, XSS e inyección de Sql.

Otros dos aspectos muy a tener en cuenta desde el inicio y durante el desarrollo de una aplicación web son los siguientes:
  • XSS (Cross site scripting): es una vulnerabilidad que pueden sufrir las aplicaciones por básicamente no controlar los datos que un usuario envía a través de formularios o como parámetros en las URL. Por ejemplo, supongamos una aplicación recibe un formulario con un nombre que se escupe tal cual se envió en otra página de la aplicación y que otros usuarios pueden visualizar en sus navegadores posteriormente cuando accedan a las páginas que los muestran. Una posible situación puede darse cuando los datos enviados se guardan en una base de datos, un usuario los envía se guardan en la base de datos y otro usuario los ve. Ese dato puede ser una cadena inofensiva como el nombre que se pide pero un usuario malicioso puede enviar código javascript o una imagen con una URL que puede recolectar con cualquier propósito la información de los usuario que la ven en su navegador. Un usuario enviando los datos adecuados puede explotar esta vulnerabilidad y conseguir desde obtener la sesión de otro usuario y hacer cualquier tipo de acción como si fuera ese otro usuario, hasta obtener datos y distribuir virus a los usuarios a través de nuestra propia página web. 
  • Inyección Sql: esta vulnerabilidad puede ser explotada también por confiar en los valores que envía el usuario pero en vez afectar al html que genera la aplicación web afecta a las base de datos que utilice la aplicación. Si usamos los parámetros enviados por una fuente no confiable para construir las sql de forma dinámica concatenando trozos de sentencia con los parámetros, un parámetro con el valor adecuado puede modificar completamente la sentencia. Concatenando elementos se puede terminar una Sql y hacer cualquier otra a continuación. Las posibilidades de esto es que se podría extraer cualquier dato o borrar completamente la base de datos con sentencias delete o drop. Por ejemplo, hacer esto tiene el problema de la inyección de Sql: "select * from producto where id = " + id. Si el parámetro id tuviese el valor "1; delete from producto;" podríamos borrar todos los datos de la tabla.
Por tanto, tanto para evitar fallos de seguridad por XSS y de inyección Sql no se debe confiar en ningún dato enviado por el usuario o de un sistema externo. En realidad en ambos problemas de seguridad la situación es el misma pero que afecta a distintas partes de la aplicación, en un caso a la base de datos (inyección Sql) y en otro a la capa de presentación de la aplicación (XSS).

¿Que hay que hacer para evitar estos problemas? Depende del caso. Para evitar XSS todo lo que se emita en el html de la página y se envíe al navegador del usuario ha de ser codificado como html haciendo que si hay un dato malicioso sea inofensivo ya que el navegador no lo interpretará como parte del lenguaje de marcas html sino como texto. Para evitar la inyección de Sql si construimos alguna dinñamicamente los parámetros no se han de añadir concatenándolos. En Java con PreparedStatement, y seguro que en cualesquiera otros lenguajes, por un lado va la sql y por otro los parámetros, la clase o API que utilicemos se encargará de ejecutar la sentencia con los parámetros adecuados sin el problema de la inyección Sql (además tiene la ventaja de que el código será más legible al no estar mezclada con los parámetros concatenados).

A continuación explicaré que funcionalidades proporciona Tapestry para que las aplicaciones desarrolladas con él sean más seguras en cuanto a XSS.

Para evitar XSS todo lo que se genere como html a partir de datos recuperados de la base de datos y enviados por un usuario hay que escaparlo. Y Tapestry hace eso por defecto por lo que salvo que de forma expresa no hagamos el escapado no tendremos problemas de XSS. La generación de html se puede hacer de dos formas: en los archivos de plantilla tml o en código Java si se trata de un componente que no tiene plantilla tml asociada.

Con una plantilla tml haremos lo siguiente y el nombre se mostrará escapado en el html:

Para evitar escapado hay que usar la opción:

En el componente Java usaremos la clase MarkupWriter y su método write para escapar los valores y el método writeRaw para evitar el escapado si estamos seguros de que no implica un problema de seguridad:

Para evitar inyección de Sql usando Hibernate, JPA o la clase PreparedStatement y separando los parámetros de la sql o hql estaremos protegidos. Las buenas prácticas y un ejemplo de mala práctica usando la API de Hibernate, hql y sql para hacer las búsquedas son las siguientes:

En la siguiente imagen puede verse como se puede inyectar javascript enviando un formulario con dato que no es escapado correctamente al mostrar su valor de nuevo.

Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.

Referencia:
Documentación sobre Apache Tapestry
Cross site scripting
Inyección SQL
MarkupWriter
Output
OutputRaw

viernes, 24 de mayo de 2013

Seguridad en aplicación web con Apache Tapestry

Apache Tapestry
Apache Shiro
Además de la persistencia en una base de datos, otra de las funcionalidades comunes que suele necesitar una aplicación es la seguridad. En la seguridad hay varios aspectos a tratar que son:
  • Autenticación: que consiste en identificar al usuario en el sistema y comprobar que el usuario es quien dice ser. Normalmente la autenticación se suele realizar pidiéndole al usuario su identificativo, nombre de usuario o correo electrónico y una contraseña que solo él conoce. Aunque hay otras formas de realizarlo entre ellas los certificados.
  • Autorización: que consiste en determinar si el usuario autenticado tienen permisos para realizar una determinada operación. La autorización puede realizarse mediante roles, permisos o una combinación de ambos dependiendo de lo adecuado para la operación. Pero en ocasiones no solo hay que validar si un usuario tiene permisos para para realizar una acción, también puede ser necesario restringir la operación sobre ciertos datos, los que se determinen que él está autorizado a modificar, si no se hiciese esto un usuario podría alterar los datos de otro y el sistema tener una brecha de seguridad.
La autenticación y la autorización son solo dos aspectos a considerar pero no son suficientes para considerar una aplicación segura. Otros aspectos a tener en cuenta son XSS (Cross Site Scripting), la inyección de Sql o las conexiones cifradas SSL/TLS pero estos serán tema para otras entradas.

La información de autenticación y autorización puede guardarse en diferentes formas en lo que se conocen como Realms comunmente en Java. Algunos Realms puede ser simples archivos de texto plano aunque por su dificultad de mantenimiento al añadir nuevos usuarios, permisos o roles y que puede requerir un reinicio de la aplicación se suele optar por opciones como una base de datos relacional, un sistema LDAP o una base de datos nosql.

Para tener un sistema seguro no basta con ocultar las opciones que un usuario no puede realizar. Ocultar las opciones está bien pero también hay que realizar las comprobaciones de autorización en el caso de una aplicación web en el servidor, al igual que no basta con realizar las comprobaciones de validación de datos en el cliente con javascript, en ambos casos las comprobaciones hay que hacerlas en el lado del servidor también, de lo contrario nada impediría a un usuario conociendo la URL y datos adecuados a enviar realizar algo que no debería (advertido estás si no quieres que te llamen un sábado de madrugada).

La seguridad puede aplicarse de dos formas o una combinación de ambas:
  • De forma declarativa: ya sea mediante anotaciones o en un archivo independiente del código. Esta es la opción preferida ya que de esta manera el código de la aplicación no está mezclado con el aspecto de la seguridad.
  • De forma programática: si la opción declarativa no no es suficiente para algún caso podemos optar por hacerlo de forma programática, mediante código, con la que tendremos total flexibilidad para hacer cosas más específicas si necesitamos aunque mezclaremos el código de la aplicación con el código de seguridad.
Para aplicar seguridad en una aplicación Java disponemos de varias librerías, entre las más conocidas están:
Las dos librerías son similares aunque se comenta que Apache Shiro es más fácil de aprender. Además de integraciones con estas librerías Apache Tapestry dispone de módulos para realizar autenticación con servicios de terceros como Facebook, Twitter o sistemas OpenID.

Pero veamos como aplicar seguridad a una aplicación web que use el framework Apache Tapestry. En el ejemplo usaré el módulo tapestry-security que a su vez usa Apache Shiro. El ejemplo consiste un una página a la que solo los usuarios autenticados pueden acceder, en ella los usuarios podrán realizar varias operaciones: sumar a una cuenta uno, dos, tres, restar uno o poner la cuenta a cero en función de los permisos y roles que tengan. Para autenticase se usa un formulario aunque perfectamente podría usarse una autenticación BASIC.

Por simplicidad en el ejemplo los usuarios, passwords, roles y permisos los definiré en un archivo de texto, aunque en un proyecto real probablemente usaríamos una base de datos accediendo con hibernate para lo cual deberíamos implementar unos pocos métodos de la interfaz Realm o si necesitamos autorización la interfaz AuthorizingRealm de Shiro. El archivo shiro-users.properties sería el siguiente:

Por una parte se definen los usuarios con su password y roles que posee y por otro se definen los roles y los permisos que tiene cada uno.

La única configuración que deberemos indicarle a Tapestry es la URL de la página que autenticará a los usuarios y la página a mostrar en caso de que el usuario no esté autorizado para realizar alguna operación y el Realm a usar, lo hacemos añadiendo el siguiente código al módulo de la aplicación:

La página que realiza la autenticación es muy simple, poco más se encarga de recoger el usuario y password introducidos en el formulario de autenticación y a través del Subject realiza el inicio de sesión.

Una vez configurado el módulo y hecha la página que realiza la autenticación solo debemos usar de forma declarativa las anotaciones que proporciona Shiro, en el caso de la página Index @RequiresUser para permitir solo a los usuarios autenticados entrar a esa página y sobre las operaciones @RequiresPermissions para requerir ciertos permisos para ejecutar las acciones o @RequiresRoles para requerir ciertos roles. Estas anotaciones podemos usarlas no solo en las páginas y componentes de Tapestry que forman parte de la capa de presentación sino también en los servicios que desarrollemos y que forman la capa de lógica de negocio.

Si las anotaciones no son suficientes podemos hacerlo de forma programática, este es el probable caso de que un usuario solo debería modificar los datos relativos a él  sin poder modificar los de otros usuarios. El código variará en función de la forma de determinar si el usuario tiene permisos para un dato. Para comprobar si un usuario tiene ciertos permisos de forma programática debemos usar el objeto Subject que tiene muchos métodos para realizar comprobaciones, como para  reinicializar la cuenta se ha de tener el permiso «cuenta:reset» se debe hacer lo codificado en el método onActionFromReset:

En la plantilla de presentación que genera el html también deberemos hacer algunas comprobaciones de seguridad, básicamente para mostrar los enlaces a los que un usuario tenga permisos.

A continuación unas capturas de pantalla de la aplicación de la página de inicio autenticación, de la página con el índice de acciones y de la página de acción no autorizada:

Para hacer uso de tapestry-security deberemos incluir la librería como dependencia en el archivo build.gradle del proyecto:

Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.

Para finalizar, a pesar de lo simple del ejemplo pero suficientemente representativo de lo que podría requerir una aplicación real comentar lo sencillo y limpio que ha sido aplicar la seguridad, por una parte gracias al uso de anotaciones y por otra gracias a Tapestry de por si.

Referencia:
Documentación sobre Apache Tapestry
http://tapestry.apache.org/security.html
http://tapestry.apache.org/
http://shiro.apache.org/
http://static.springsource.org/spring-security/site/index.html

viernes, 17 de mayo de 2013

Copia de seguridad con rsync

rsync
Las copias de seguridad son imprescindibles, si alguien piensa que no es que nunca se le ha estropeado el ordenador ya sea porque ha fallado el hardware o por una actualización que ha corrompido el sistema de arranque o peor por que el disco duro se ha estropeado, eso o no tiene datos importantes que tener a buen recaudo. Si aún no te ha pasado que se haya estropeado el ordenador y te has quedado sin esa información que no quieres perder quizá hayas tenido suerte pero has de saber que tarde o temprano te pasará y para no perder esa información los «backups» son imprescindibles.

Una vez que tener copias de seguridad nos parece importante y tenemos respondida la pregunta de por qué, se nos plantean algunas otras preguntas:
  • ¿De que hacer copias de seguridad?
  • ¿Cada cuanto hacer copias de seguridad?
  • ¿Que características han de tener las copias de seguridad?
  • ¿En que medio guardar las copias?
  • ¿Como hacer las copias de seguridad?
  • Y tan importantes como las anteriores, ¿como recuperar los datos?
Cada pregunta tiene diferentes respuestas según el usuario, entorno o la información de la que hacer las copias de seguridad. A la pregunta de que hacer copias de seguridad cada uno deberá conocer que información no quiere perder en caso de desastre, cuanto cambian los datos y que se perdería en un periodo de tiempo (día, semana, mes, ...), si las copias deben estar comprimidas para ahorrar espacio o cifradas y finalmente dependiendo de algunas respuestas anteriores que herramienta o herramientas usar.

Yo hasta ahora no usaba ninguna herramienta usaba el simple y manual método de copiar y pegar archivos del disco duro del ordenador a un disco duro externo. Cuando no tenía disco duro externo las copias de seguridad las hacía en CD y DVD pero hacerlas en un medio óptico como los anteriores no los recomiendo porque son lentos de realizar, en estos días no tienen capacidad suficiente para guardar todo y por tanto deberemos tener varios volúmenes y finalmente lo más importante porque son unos medios poco fiables. Dado el precio y capacidad que tienen hoy en día los discos duros USB externos además de su velocidad de transferencia es más recomendable esta opción y es la que a día de hoy uso. Pero aún con un disco duro externo el método de copiar y pegar tarda un buen tiempo y sigue siendo lento, con 70 GiB de archivos personales que no quiero perder, pero también el hecho de hacerlo manualmente es propenso a que algún día se me olvide hacer la copia de seguridad de todo y por la ley de Murphy ese día fallará el ordenador. Con lo que con esta situación he empezado a buscar alternativas a mi método manual de copiar y pegar.

Empece probando Déjà Dup pero no me ha convencido porque las copias de seguridad las guarda en un formato comprimido (y cifrado si se quiere) de modo que para recuperar los archivos se ha de utilizar la misma herramienta. Para mi una de las características de la copia de seguridad es que siga pudiendo tener accesibles los archivos independientemente de la herramienta. Viendo las posibilidades de los programas de copia de seguridad disponibles en Linux por la que me he decantado ha sido rsync. Es un método simple que mantiene sincronizados el contenido de dos carpetas, copiando los nuevos archivos, borrando los eliminados y solo actualizando los necesarios además de poder hacerse las copias entre dos máquinas diferentes a través de la red. Mediante rsync para hacer una copia de seguridad ahora tardo mucho menos ya que solo se copian los archivos que hayan cambiado además de estar automatizado con un script con lo que resulta más fácil y por ello se pueden hacer más a menudo.

Con el siguiente comando de rsync se puede hacer una copia de seguridad de una carpeta a otra:

La opción -a es una meta opción de utilidad que engloba otras con las más comunes para hacer copias de seguridad, -P hace que se muestre el progreso, --delete borra los archivos en la carpeta de destino que se hayan borrado en la carpeta origen.

Otro ejemplo para copiar varias carpetas de la carpeta home de un usuario es, con la opción --files-from, hay que indicar también la opción -r para que las carpetas se copien de forma recursiva:

Este simple comando ahorra mucho tiempo ya que con él no hay que copiar los XXX GiB cada vez que hay que hacer un copia de seguridad, solo se copiará lo que haya cambiado entre la copia de seguridad y el origen, que muy probablemente es una cantidad mucho menor que esos XXX GiB. Con lo simple que es se me hace extraño que haya pasado tanto tiempo haciendo las copias de seguridad manualmente.

Si quieres algunas otros ejemplos de uso de rsync como por ejemplo el como programar las copias de seguridad con cron o quieres conocer otras herramientas en la wiki de Arch Linux tienes unas cuantas más, algunas con interfaz gráfica.

Referencia:
https://wiki.archlinux.org/index.php/Backup_Programs
https://wiki.archlinux.org/index.php/Rsync

viernes, 10 de mayo de 2013

Modificar la base de datos con Liquibase

Liquibase
A lo largo del ciclo de desarrollo de todo proyecto se producen cambios algunos de los cuales casi con toda seguridad afectarán al esquema o modelo de la base de datos. Por una parte se pueden producir modificaciones de definición de datos (DDL, Data Definition Language) para añadir y borrar tablas y añadir, modificar y eliminar campos, crear índices y restricciones de integridad. Por otra parte se pueden producir manipulaciones de datos (DML, Data Manipulation Language) para añadir nuevos datos, modificar o eliminar algunos de los existentes. Estos cambios de la base de datos también puede ser útil que estén bajo el sistema de control de versiones que empleemos al igual que hacemos con los cambios del código fuente del programa para poder volver a un estado anterior. Una herramienta que nos permita hacer «refactor» de forma automatizada de la base de datos nos facilitará la tarea.

Para llevar a cabo estas modificaciones tenemos varias opciones la primera que se nos puede ocurrir es crear un archivo con las sentencias SQL de los cambios y lanzarlo contra la base de datos. Sin embargo, esta opción aunque sencilla y rápida puede que no sea la mejor opción. La primera desventaja es que las sentencias puede que solo funcionen en uno o unos pocos sistemas de base de datos con lo que tal vez tengamos que desarrollar varios scripts para cada uno de los sistemas de base de datos a los que debamos dar soporte. La segunda desventaja es que si queremos revertir los cambios de un «refactor» deberemos crear las sentencias de «rollback» que puede dar bastante trabajo nuevamente si tenemos diferentes DBMS. Además de estas desventajas podríamos necesitar volver a cierto estado de la base de datos deberemos gestionarlo de alguna manera nosotros mismos probablemente de forma manual.

Una de las herramientas que nos puede hacer más sencillo estas modificaciones que se producen a la base de datos es Liquibase. Los scritps que actualizan la base de datos se definen en un XML (o json entre otros) de cambios que es independiente del sistema de base de datos que usemos aunque se pueden incluir sentencias SQL específicas para uno de ellos, también se pueden definir las sentencias rollback que permiten volver a un estado anterior. Soporta las principales bases de datos entre ellas:
  • MySQL
  • PostgreSQL
  • Oracle
  • MS-SQL
  • SQLite
  • Y otras entre ellas Sybase Enterprise, Sybase, DB2, Apache Derby, HSQL, H2, Informix, InterSystems, Firebird, SAPDB.
Una de las primeras cosas que podríamos necesitar si empezamos a usar esta herramienta sobre una base de datos existente es obtener el primer xml changelog.xml con el estado actual de la base de datos.


Si queremos lanzar una actualización de la base de datos a partir de un archivo changelog.xml con algunas modificaciones ejecutaremos lo siguiente:


Para obtener las SQLs que son lanzadas en la consola sin que sean ejecutadas contra la base de datos usaremos «updateSQL» en vez de «update» de esta manera podremos revisar las sentencias que ejecutará Liquibase. También puede sernos de utilidad obtener las diferencias entre dos versiones de la bases de datos o hacer rollback de los cambios, cosa que podemos hacer de diferentes formas.
Un archivo changelog.xml tiene el siguiente aspecto, en él vemos como se definen las tablas, campos y restricciones de integridad:

Por supuesto, estas tareas se pueden automatizar con la herramienta de construcción que prefiramos ya sea Ant, Maven, Gradle o mediante linea de comandos.

Referencia:
Liquibase

viernes, 3 de mayo de 2013

Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon

Jasmine
Backbone.js
Tener un conjunto de pruebas unitarias sobre el código de la aplicación que desarrollamos es importante para tener más seguridad de que lo que hacemos funciona como se espera y no introducimos errores en funcionalidades que ya existían al hacer modificaciones, además de que en el caso de que haya errores estos serán más fácilmente descubiertos y resueltos en menos tiempo lo que hará que seamos más productivos. Si en una aplicación web es posible que ya hagamos pruebas unitarias sobre el código del lado del servidor, ¿por que no hacerlo también en el lado del cliente? Si la aplicación tiene un peso importante de javascript en el lado del cliente, y esa es la tendencia, es recomendable tener también teses unitarios de esta parte del código.

Aprovechando el ejemplo que hice de una aplicación bastante completa de una lista de tareas que utilizaba Backbone, RequireJS, Mustache en el lado cliente y Tapestry y RESTEasy en el lado del servidor ahora le añadiré al código javascript el conjunto de pruebas unitarias con Jasmine y Sinon.

Jasmine es una herramienta para realizar pruebas basadas en BDD (behavior-driven development). BDD es parte de las metododologías TDD (test-driven development) haciendo énfasis en que la gente del dominio pueda trabajar con la gente técnica. Jasmine nos servirá para hacer las pruebas unitarias, Sinon nos permitirá usar spies, stubs y mocks en esas pruebas:
  • Spy: Un spy es una función que se envía como parámetro al sujeto bajo prueba para que recolecte datos sobre lo que sucede dentro de esa función y objeto, se llama espía porque se envía tras la lineas enemigas para que recolecte información. La información que puede recolectar es el número de veces que se llama una función, con que parámetros y cuales, los valores de retorno o si lanzó excepciones.
  • Stub: ofrecen un comportamiento parcial preprogramado del objeto real, se puede utilizar para que proporcione los datos que queramos al sujeto bajo prueba. Los stubs también pueden ejercer de espías.
  • Mock: que también son espías (funciones falsas) y stubs (ofrecen comportmiento preprogramado) conocen las expectativas de como han de ser usados, requiriendo que se llamen con ciertos parámetros, un número de veces, en cierto orden, .... Conocen cual es el comportamiento esperado del sujeto bajo prueba y una vez hecha la prueba se puede comprobar.
A continuación veremos el código de las pruebas para la aplicación, las pruebas se centrarán en el código en el código de la lista de tareas y de probar las diferentes partes por separado, el modelo, vista y controlador. Veamos primeramente las pruebas para los modelos Tarea y Tareas, las pruebas se centrarán en las funciones adicionales que hemos asociado a los modelos, es decir, probaran lo que hemos hecho no Backbone.

En la parte del controlador de la vista nos centraremos en probar los métodos y eventos como onChangeCompletada en TareaView y addTarea, resetTareas, onClickLimpiar y onKeypressNuevaTarea en TareasView. Dado que la lista de tareas hace uso de un servicio REST utilizaremos Sinon para atrapar esas peticiones AJAX que se harían en la realidad.

En la vista probaremos que dado un modelo el resultado del html contiene los elementos que esperamos:

Las pruebas se lanzan abriendo la página html SpecRunner.html en el navegador. Esa página html tiene la dependencia sobre RequireJS y este carga en el navegador el resto de dependencias que necesiten los diferentes módulos incluido el módulo bajo prueba de la lista de tareas. Las pruebas de Jasmine se definen en otro módulo de RequireJS que tiene como dependencia la librería de Jasmine, Sinon y por supuesto el módulo tareas que probará. El resultado es el siguiente:
Backbone se presta muy bien a las pruebas unitarias. Una gran característica es que para realizarlas no es necesario que las vistas se asocien a un DOM real, funcionan en memoria, de modo que no hace falta insertar ni quitar contenido de la página mientras se van ejecutando los teses, no hay necesidad de limpiar el DOM para los siguiente teses y así funciona más rápido. Como usa el patrón MVC cada una de las partes del modelo, vista y controlador pueden probarse por separado. Este ejemplo no muestra todas la posibilidades de Jasmine y Sinon pero da una idea de como son las pruebas con ellas.

En mi repositorio de GitHub puedes encontrar el código fuente completo del ejemplo de pruebas unitarias en javascript con Jasmine y Sinon.

Y con esta es por el momento la última entrada sobre javascript que escriba de esta serie, no descarto escribir alguna más sobre javascript en el futuro porque alrededor de este lenguaje de programación están surgiendo muchas utilidades y librerías.

Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript
Optimizar módulos de RequireJS y archivos Javascript
Patrón de diseño MVC del lado cliente con Backbone.js
Introducción y ejemplo de Backbone.js