viernes, 25 de octubre de 2013

Relaciones jerárquicas en bases de datos relacionales

Otro de los problemas que se suele presentar al trabajar con bases de datos relacionales además de como internacionalizar las entidades del dominio o como hacer búsquedas de texto completo es como modelar las relaciones jerárquicas. Para resolver el problema de las búsquedas en las bases de datos relacionales con datos jerárquicos hay varias soluciones posibles cada una con sus ventajas y desventajas y una más ideal si la base de datos lo soporta, son:

Listas adjacentes

En este modelo se crea una campo adicional que indicará el nodo padre de la relación jerárquica, los nodos raíz tendrán este campo a null al no tener padre.

Buscar los descendientes de un nodo, sin el soporte de quieries recursivas y suponiendo una profundidad máxima en la jerarquía de diez se puede conseguir con la siguiente sql:

En este caso obtendremos una fila con su jerarquía por cada hoja del árbol. Todo el conjunto de identificativos obtenidos forman los descendientes. Hay que tener en cuenta que en los resultados un identificativo puede aparecer varias veces y con esta consulta el nodo del que se buscan descedientes está incluido.

Buscar los ascendientes se puede hacer de forma similar:

Con esta sql obtendremos una fila con los identificativos, c1 será el identificativo del nodo superior y c10 el nodo inferior de la jerarquía.

Con esta solución para mover un nodo de un padre a otro en el árbol basta con actualizar el identificativo del nodo padre, es simple y rápido. Sin embargo buscar descendientes y ascendientes es más complejo e ineficiente si la base de datos no soporta queries recursivas (que las bases de datos más importantes, Oracle, SQL Server, PosgreSQL salvo MySQLsoportan y a partir de laversión 5.6 ya lo hace), también puede requerir una segunda query para buscar los datos de los descendientes y ascendientes, con estas solo recuperamos los identificativos.

Conjuntos anidados

Esta solución se basa en que cada nodo de la jerarquía esté numerado, el padre tendrá dos campos el número de menor hijo y el número del mayor hijo, todos los nodos cuyos números estén entre esos dos números son descendientes del nodo padre. La consulta de buscar los nodos descendientes es simple y eficiente.

Buscar los nodos ascendientes también se puede conseguir una sql eficientemente:

La desventaja de esta solución está en el momento que queremos insertar un nuevo nodo en el árbol de la jerarquía o mover un nodo dentro del árbol ya que implica reorganizar los valores de las columnas left y right, puede que de muchas filas y por tanto resultar lento.

Consultas recursivas

Con el soporte de consultas recursivas se puede conseguir la simplicidad de las adjacency list y la eficiencia de los nested sets. El modelo de datos es similar al caso de las Adjacency List con una columna del identificativo padre del nodo.

Para buscar los descendientes de un nodo sería:

Para buscar los nodos ascendientes:

Como comentaba de las bases de datos más importantes de entre Oracle, SQL Server, PostgreSQL y MySQL solo MySQL no lo soporta aunque a partir de laversión 5.6 también lo hace. Dependiendo de si hacemos más consultas que modificaciones y de si queremos complicarnos más con los nested sets deberemos optar por una solución u otra, en cualquier caso optaremos por las queries recursivas si la base de datos lo soporta.

Referencia:
http://en.wikipedia.org/wiki/Hierarchical_query
http://en.wikipedia.org/wiki/Nested_set_model
http://en.wikipedia.org/wiki/Adjacency_list_model

http://vadimtropashko.wordpress.com/2008/08/09/one-more-nested-intervals-vs-adjacency-list-comparison/
http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-database
http://explainextended.com/2009/09/24/adjacency-list-vs-nested-sets-postgresql/
http://www.postgresql.org/docs/8.4/static/queries-with.html

viernes, 18 de octubre de 2013

Solución al problema de seguridad CSRF en Apache Tapestry

Apache Tapestry
Al desarrollar una aplicación el problema de seguridad XSS (Cross-site scripting) puede afectar a los usuarios de nuestra aplicación. XSS consiste en que la aplicación permite la inyección de código malicioso que posteriormente otros usuarios obtienen al acceder a nuestra aplicación. Se produce porque nuestra aplicación no realiza el escapado correctamente de lo que envía al usuario y porque el usuario confía en lo que obtiene del servidor. Para evitar este problema Apache Tapestry por defecto realiza un escapado de lo que emite al cliente con lo que si no hacemos lo contrario la aplicación estará a salvo de este problema.

Otro problema de seguridad es CSRF (Cross-site request forgery) en el que básicamente un sitio al que se accede devuelve un enlace malicioso que provoca una acción en otro, el atacado. El enlace devuelto puede producir cualquier acción que el sitio atacado permita, el ejemplo que se suele poner es el de un sitio bancario y el intento de hacer una transferencia de la cuenta del usuario que tiene iniciada una sesión en la página de su banco a la cuenta del atacante pero podría ser la realización de un cambio de contraseña a una que conozca el atacante y de esta forma posteriormente este pueda autenticarse con la cuenta de ese usuario en el sitio atacado. En la wikipedia este problema de seguridad está más ampliamente explicado con ejemplos, limitaciones y como prevenirlo. A diferencia de XSS donde el usuario confia en lo que obtiene del servidor en el caso de CSRF es al contrario, el servidor confia en las peticiones del cliente, aunque puedan provenir de un sitio malicioso.

Una solución que suele aplicarse para resolver el problema de CSRF es incluir en los enlaces y formularios un token de seguridad, si el token no se envía o no se corresponde con el token del servidor la petición se considera inválida y no se procesa. Al acceder a una página se genera el token de seguridad y se incluye en todos los enlaces y formularios, este token no será conocido por un a tercera parte y los enlaces maliciosos no serán procesados.

En esta entrada mostraré como solucionar este problema en Tapestry con una combinación de mixin, anotación, advice y objeto de estado de aplicación (SSO), similar a lo explicado en este blog pero con la adición que no solo sirve para formularios sino también para enlaces y el componente BeanEditForm.

Primero veamos el objeto estado de aplicación que contendrá el token (sid) de seguridad, lo generará y lo validará, este objeto de estado de aplicación se guardará a nivel de sesión de modo que el token que se envía en la petición pueda ser validado con el token guardado en este SSO.

El mixin CSRF hará que en los formularios se incluya un campo oculto con el token se seguridad del SSO y en los enlaces se incluya como un parámetro. El nombre del parámetro en ambos casos será t:sid. Este mixin puede ser aplicado a los componentes Form, ActionLink, EventLink y BeanEditForm.

Creamos una anotación para marcar los manejadores de evento y métodos donde queremos que aplique la seguridad CSRF.

Para aplicar la seguridad en los manejadores de evento y métodos marcados con la anotación lo haremos con cierta metaprogramación, con ella comprobaremos que el token del SSO se corresponda con el token enviado por la petición. Esta forma de metaprogramación es lo que en Tapestry se conoce como Advice, la funcionalidad consistirá en si el token de seguridad es correcto se permite la invocación al método protegido, si el token no es correcto se lanza una excepción impidiendo la llamada al método protegido.

Finalmente, debemos modificar el módulo de nuestra aplicación para dar a conocer a Tapestry el Advice para aplicar y comprobar la seguridad con el parámetro t:sid enviado y en el objeto SSO.

Para que en los componentes sea incluido del token de seguridad haremos uso del mixin, es tan simple añadir el atributo t:mixins y el mixin csrf en los elementos a incluir el parámetro t:sid:

Y para proteger los manejadores de evento con la anotación Csrf de la siguiente manera:

Con todo esto podemos resolver el problema de seguridad CSRF de forma muy simple y de forma declarativa con una combinación de mixin y anotaciones, solo son necesarios ¡20! caracteres entre ambos.

Esta es la sección de la aplicación del ejemplo funcionando donde puede probarse el mixin y ver la diferencia del comportamiento sin el mixin aplicado.

Cuando pulsamos en el enlace que envía un t:sid inválido mediante un petición ajax provocará el siguiente informe de error con un mensaje descriptivo de lo que ha ocurrido.


En las siguientes imágenes puede verse el parámetro t:sid que se añade tanto a un formulario como a un enlace cuando le aplicamos el mixin csrf.
Parámetro t:sid en un formulario

Parámetro t:sid en un enlace

¿Ya tienes en cuenta el problema de seguridad CSRF en tus aplicaciones web? ¿Con el framework que uses aplicar una solución es tan simple como esta?

De esta entrada aparte de resolver el problema de seguridad CSRF quiero destacar con se realiza la metaprogramación en Apache Tapestry con las clases Plastic de Tapestry en la clase CsrfWorker.

Si te ha parecido interesante esta entrada puedes descargar el libro PlugIn Tapestry en el que explico más en detalle como desarrollar aplicaciones web en Tapestry y en el que descubrirás como resolver problemas comunes como este en las aplicaciones web de una forma tan buena como esta.

Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip.


Referencia:
Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet
Apache Tapestry - CSRF Protection - First Prototype
http://wiki.apache.org/tapestry/Tapestry5CSRF
https://code.google.com/p/gsoc2011-csrf-protection

viernes, 11 de octubre de 2013

Solución al doble envío de peticiones en aplicaciones web

Apache Tapestry
Las aplicaciones web pueden recibir información del usuario a través de formularios o en los parámetros de los enlaces. Los formularios o enlaces pueden desencadenar en la aplicación diversas acciones. Dada la naturaleza de las aplicaciones web en ciertas ocasiones la latencia de la red o el tiempo que toma procesar la petición un usuario puede tener la oportunidad de hacer clic varias veces en un botón o enlace también cuando ve que tarda mucho en obtener la respuesta o simplemente por su desconocimiento al estar acostumbrado a hacer doble clic al trabajar con otras aplicaciones o la interfaz de su sistema operativo. En ocasiones el problema puede producirse también haciendo un refrescar con el botón del navegador si no se hace un redirect después del envío del formulario (Tapestry nos evita esto ya que hace un redirect después de cada envío de un formulario o acción, lo que se conoce como redirect after post o post/redirect/get).

El caso del doble o N-clic puede ser un problema, imagina una aplicación que borra un registro de la base de datos o una aplicación que realiza el cobro de una tarjeta de crédito. Si el usuario tienen la oportunidad de hacer doble clic en el botón o enlace enviándo dos peticiones al servidor lo más probable es que solo una de ellas funcione, la segunda petición puede producir una excepción en el servidor o peor aún dependiendo de la acción un efecto indeseado. ¿Eres consciente y tienes en cuenta que puede pasar en tu aplicación si se da esta situación?

Una de las formas de evitar este problema en cierta medida es desactivando el botón o enlace una vez que se ha pulsado y el formulario se envía o se realiza la petición. Podemos hacer uso de un código javascript que en el momento de pulsar el botón o enlace impida que desencadene su acción por segunda vez, por ejemplo desactivándolo.

En Tapestry podemos usar un mixin para reutilizar este código en todos los sitios que necesitemos de una forma muy sencilla. En el contexto de Tapestry un mixin es un componente que no se usa de forma individual sino que se usa junto a otro componente y que añade cierta funcionalidad al componente asociado. En la aplicación de demostración JumpStart se puede ver el código del componente mixin ClickOnce que en ciertos escenarios puede evitar el problema del N-clic. Sin embargo, la solución del mixin ClickOnce es válido solo para peticiones no ajax ya que una vez que el botón o enlace es pulsado se desactiva completamente, en una petición ajax el botón debería volverse a activar una vez que la petición Ajax termina.

A continuación mostraré el código de un mixin que mejora el ClickOnce resolviendo el problema N-clic tanto para peticiones ajax como no ajax. Por una parte tendremos el código Java asociado al mixin.

Y el javascript como un módulo de RequireJS que produce la desactivación del enlace o botón en el cliente una vez que es pulsado y monitoriza las peticiones Ajax para bloquear una segunda petición al servidor.

Este código javascript podría ser aprovechado después de haberlo modificado ligeramente para ser usado con otro framework de aplicaciones.

Una vez desarrollado el mixin podemos usarlo en un botón o enlace de la siguiente forma, a partir de este momento dar solución a este problema nos cuesta 20 caracteres en cada enlace o botón, los necesarios para aplicar el mixin con el atributo t:mixins, pero no solo eso, más importante aún que el número de caracteres, aplicarlos no nos ofusca el código:

Esta solución funciona en el lado del cliente, con javascript desactivado o un usuario malicioso podría producir el doble envío del formularios de forma intencionada. Otra solución en el lado del servidor que se suele utilizar es un token de un solo uso en la que solo se procesa el formulario con el token correcto, el token se envía al cliente en un campo oculto y se guarda en sesión, una vez que el formulario se envía al servidor se comprueba que el token sea el guardado en sesión, esta solución realiza la comprobación en el servidor pero requiere del uso de la sesión para guardar el token. La solución mediante el token puede evitar el problema CSRF donde una petición maliciosa puede realizar una acción sin el consentimiento del usuario. En otra entrada explicaré como solucionar el problema CSRF con el framework Tapestry.

Esta es la sección de la aplicación del ejemplo funcionando donde puede probarse el mixin y ver la diferencia del comportamiento sin el mixin aplicado.

En este ejemplo también puede verse como con tapestry se pueden actualizar varias zonas (la zona de la cuenta) de una página de forma simultánea de una página web con una única petición Ajax.

Si te ha parecido interesante esta entrada puedes descargar el libro PlugIn Tapestry en el que explico más en detalle como desarrollar aplicaciones web en Tapestry y en el que descubrirás como resolver problemas comunes como este en las aplicaciones web de una forma tan buena como esta.

Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip.

Referencia:
Libro PlugIn Tapestry
http://jumpstart.doublenegative.com.au/jumpstart/examples/javascript/creatingmixins1
http://stackoverflow.com/questions/17830171/mixins-in-tapestry5

viernes, 4 de octubre de 2013

Archivo de log de sentencias SQL en MySQL o MariaDB

MySQL
MariaDB
Muchas aplicaciones utilizan para su capa de persistencia un framework ORM que abstraen al programador de la base de datos, de las peculiaridades de cada una ellas y de las sentencias SQL que se envían a la base de datos para realizar las operaciones de recuperación, actualización, borrado y búsqueda. En Java el ORM suele ser Hibernate, en Symphony es Doctrine, en Ruby on Rails es ActiveRecord, en Django es Models pero en definitiva todas sirven para el mismo propósito que es darnos un marco de trabajo más similar a un lenguaje orientado a objetos y hacernos olvidar el lenguaje SQL que hay que utilizar para comunicarse con cualquier base de datos relacional.

Sin embargo, esa facilidad que dan los ORM puede hacer que las SQL que realmente se envían a la base de datos no sean eficientes o no sepamos el número exacto de las que se están enviando (problema N+1) lo que puede hacer que ciertas funcionalidades de una aplicación sean lentas y poco eficientes con un volumen de datos grande.

En MySQL y MariaDB se puede obtener un archivo de log con todas las sentencias que se están enviando. Además también se puede obtener un log con aquellas sentencias que tardan en ejecutarse más tiempo de que estimemos como lento para una sentencia. Para obtener estos archivos de log debemos modificar la configuración de MySQL o de MariaDB que en Linux está en el archivo /etc/mysq/my.cnf. Al final de la sección [mysqld] debemos añadir las siguiente linea para obtener un archivo con todas las sentencias:

Y lo siguiente para obtener un archivo con solo las sentencias lentas, en el caso del ejemplo las que tardan en ejecutarse más de un segundo:

Para que MySQL pueda escribir sobre estos archivos debemos crear los archivos y asignarle permisos al usuario con el que se ejecuta MySQL con:

Una vez modificada la configuración y creados los archivos de log con los permisos adecuados debemos reiniciar el servicio de MySQL.
Estos los nos ayudarán a identificar sitios con el problema de N+1 al acceder a una relación 1 a N entre dos objetos o si es necesario que creemos algún índice para hacer más rápidas ciertas consultas si vemos que algunas están saliendo en el log de sentencias lentas.

Referencia:
http://www.redips.net/mysql/query-logging/
http://dev.mysql.com/doc/refman/5.1/en/slow-query-log.html
http://stackoverflow.com/questions/6614429/logging-mysql-query-on-ubuntu