Actualidad

04/08/2020

/ , , , ,

La evolución de las aplicaciones: camino a los microservicios

La evolución de las aplicaciones siempre ha estado estrechamente relacionada con la evolución tecnológica y las necesidades de los usuarios y las empresas.

Del mismo modo que las organizaciones empresariales evolucionan en la forma de hacer las cosas, las aplicaciones informáticas deben seguir el mismo camino para lograr su único objetivo, satisfacer dichas necesidades y servir al usuario de la forma más eficiente. Es como un ciclo que se retroalimenta constantemente:

Esta evolución ha obligado a un cambio en los sistemas, adaptando las infraestructuras que ofrecen la disponibilidad de dichos servicios y aplicaciones, de tal forma que los usuarios que los consumen lo puedan hacer de una forma más eficiente, segura y además en remoto, es decir, disponible en internet.

Un ejemplo sencillo para mostrar que adaptarse en esta evolución es obligatorio, sería una aplicación de mensajería que tiene un éxito inesperado. Es cierto que el aumento de demanda no es sino el principio del éxito, pero también puede ser el principio del fracaso si la infraestructura que soporta dicha aplicación de mensajería no está preparada para soportar tanta demanda. En este caso, la aplicación está condenada al fracaso.


Aplicación monolítica

La característica principal de estas aplicaciones es que hacen uso de una base de código única para sus servicios y funcionalidades. Es decir, la aplicación es «responsable» de todas las tareas necesarias para realizar una determinada función.

Este tipo de aplicaciones se destacan por combinar la interfaz del usuario y la capa de acceso a los datos en el mismo programa y alojarlos en la misma máquina (o nodo).

Una de las características para tener en cuenta es el llamado escalado vertical. Si el servidor que aloja la aplicación recibe un aumento en las peticiones de usuario, es posible que los recursos existentes en la máquina sean insuficientes. La única posibilidad es aumentar los recursos en dicha máquina (RAM, espacio en disco, etc.) dentro de sus límites.

Goza de una arquitectura sencilla, puesto que toda la aplicación se aloja en el mismo nodo, donde las comunicaciones entre las distintas partes de la aplicación siempre son de forma local.

Debido a su arquitectura, las interferencias entre los componentes de la aplicación son más delicadas, y un fallo en una de sus partes puede poner en evidencia toda la aplicación.

Otra desventaja, la tenemos en la complejidad de las actualizaciones, que pueden afectar a la disponibilidad de todo el sistema durante un período de tiempo durante su despliegue.

Por el simple hecho de tener una infraestructura estática y fija por años, el mantenimiento es más costoso y es más difícil evolucionar de forma eficiente a las nuevas necesidades del mercado.

En definitiva, aunque son fáciles de desarrollar, una aplicación que aglutina toda su funcionalidad no es la mejor opción si queremos aspiraciones crecimiento complejas, más usuarios, más desarrolladores, etc.


Aplicación distribuida

Su principal cometido es tener la aplicación disgregada en múltiples nodos, dando la posibilidad de que en cada nodo exista un componente de la aplicación o un mismo componente replicado en varios nodos.

Esta arquitectura es bastante más compleja y difícil de gestionar y administrar que una aplicación monolítica. De hecho, se deben utilizar numerosas herramientas que antes no eran en absoluto necesarias. Pero esta idea de tener la aplicación distribuida en varios nodos fue un punto de inflexión, que dio lugar a conceptos muy importantes y ha favorecido de forma incuestionable la existencia de aplicaciones mucho más robustas, escalables, eficientes y seguras.

Uno de los grandes conceptos que introduce este tipo de aplicaciones es el escalado horizontal. Es decir, cuando por cuestiones de demanda los recursos actuales ya no son suficientes, en vez de aumentar los recursos de un nodo (escalado vertical), lo que se hace es incrementar el número de nodos dedicados al procesamiento de ese componente demandado. Esto es una gran ventaja ya que el límite de recursos ya no será el soportado por un nodo, sino el soportado por tantos nodos como tengamos en nuestra arquitectura.

Otro gran concepto que surge, aunque todavía no de forma automática y madura, es la llamada elasticidad. Es decir, al igual que se aumentan el número de nodos dedicados para soportar un aumento de demanda, se pueden reducir si la demanda disminuye y dejarlos disponibles para otros procesos. Este concepto contribuye a un reparto de recursos más eficiente.

La seguridad y la robustez mejoran notablemente, ya que es más probable que los problemas de seguridad de un nodo sean cercados en ese nodo, no poniendo en riesgo toda la aplicación si alguno de ellos se pone en evidencia.

Otra gran ventaja con respecto a las aplicaciones monolíticas es que las actualizaciones no conllevan desplegar toda la aplicación ya que, en este caso el despliegue será a nivel de nodo. El mantenimiento es mucho más fácil y la durabilidad de la aplicación será mayor por la facilidad de adaptación a los cambios.

Todas estas características hacen que las aplicaciones distribuidas sean ideales cuando adecuar los recursos que se deben utilizar en función de la demanda sea clave.


Arquitectura SOA

Esta arquitectura fue la que insistió en la utilización de aplicaciones distribuidas y orientadas a servicios.

La idea fue hacer que esos servicios fueran independientes, y las interacciones entre ellos se hicieran bajo ciertos protocolos estandarizados de comunicación, como WSDL y SOAP.

Esta independencia hacía posible que servicios desarrollados por diferentes tecnologías, pudiesen comunicarse entre sí, sin ningún problema.

Debido a que los servicios son más independientes, cambios en ciertos componentes de la aplicación no alteraban al resto, simplemente debían seguir cumpliendo los criterios de comunicación establecidos.


Cloud Native (Cloud Computing)

La arquitectura SOA está más enfocada a la arquitectura de la aplicación. Cloud Native se enfoca más en la arquitectura del sistema que alberga, distribuye y ofrece las aplicaciones.

Este punto de vista, lo que ofrece es básicamente el uso de recursos en la nube bajo demanda automatizada. Es decir, va a existir una elasticidad gestionada de forma automática por el sistema en la infraestructura dedicada a cada una de las aplicaciones que distribuyen. Consiguiendo que el uso de los recursos sea lo más eficiente posible.

Estas infraestructuras dinámicas dedicadas hacen posible que las aplicaciones sean resilientes antes situaciones adversas, por ejemplo, por demanda extrema, errores hardware, etc.


Arquitectura de microservicios

No existe una definición formal pero básicamente es una evolución de SOA y está orientada al trabajo con servicios muy pequeños e independientes.

El objetivo es aislar los distintos componentes de una aplicación con el fin de que cada uno sea una aplicación por sí misma.

Otra de las diferencias con SOA es la comunicación entre los servicios; Ya no sería con servicios web WSDL o SOAP, sino vía HTTP con API-REST.

Muchos beneficios suelen asociarse a los Microservicios, pero tres de los más importantes son: una entrega más rápida, escalabilidad mejorada y una mayor autonomía.

Los microservicios son el facilitador de una mayor agilidad en términos de adaptación a los cambios del mercado. Su filosofía es compatible y está directamente relacionada con procesos ágiles de desarrollo: entrega continua. Es decir, el mantenimiento de la aplicación conlleva modificaciones en los microservicios, pudiendo llevar a cabo muchas subidas a producción en un corto período de tiempo de forma independiente (continuous deliverydeploymentimprovement).

Esto aumenta la agilidad del software porque cada microservicio se convierte en una unidad independiente de desarrollo, implementación, operaciones, versiones, y escalamiento.

En realidad, aquí el término escalabilidad es algo ambiguo. Podría referirse, por ejemplo, a la escalabilidad del tiempo de ejecución del sistema, a su adaptabilidad (a un costo razonable), o a los cambios en el número de usuarios que acceden a él. O también podría referirse a poder aumentar el número de microservicios sin interferencias con los otros o a la capacidad del proceso de desarrollo para acomodar a muchos desarrolladores trabajando en paralelo.

Lo que está claro es que, con los microservicios la unidad a escalar es el servicio. Por tanto, cada uno de ellos son una unidad autónoma que pueden ser desarrollados, desplegados y operados por un equipo diferente. Por eso, suelen implementarse sobre contenedores: un microservicio en un nodo.

En estos casos, es de máxima importancia sincronizar una responsabilidad compartida entre el desarrollador y el sistema que lo alberga. Esta es la base de la filosofía DevOps.

La antigua filosofía introducida hace décadas por el principio «Divide y vencerás» ha sido puesta en práctica en innumerables ocasiones: en los algoritmos informáticos, en el envío de pequeños paquetes de datos en el protocolo IP, en la agrupación de las tareas en 7 capas en la arquitectura de red OSI, etc.

Es curioso como la misma filosofía se sigue adoptando en las infraestructuras tecnológicas actuales. Divide and win my friend 🙂



Nota. Los diagramas de las arquitecturas monolítica y de microservicios pertenecen al blog de Chris Richardson. Te animo a que lo visites para leer sus interesantes artículos.

Actualidad

01/07/2020

/ , , , , ,

API testing con REST Client para VS Code

En los proyectos ágiles, a la vez que se acortan los ciclos de desarrollo se vuelven todavía más importantes las pruebas de nuestro API (Application Programming Interface) y se convierte en obligatoria la ejecución de pruebas automáticas.

En API testing utilizamos herramientas de software para enviar llamadas a la API, obtener resultados y registrar la respuesta del sistema. Existen muchos artículos para conocer las principales recomendaciones para este tipo de testing así como diferentes herramientas, tanto libres como de pago: Postman, Insomnia, Ping ApI, HP QTP, vREST, JMeter…

Este tipo de herramientas son de gran utilidad para poder interactuar con APIs y, como desarrolladora front-end que soy, poder hacer pruebas de los servicios que se tienen que implementar.

Probablemente las más usada o conocida sea Postman, pero en este artículo me gustaría hablaros un poco de REST Client que nos permite realizar llamadas HTTP desde el propio editor Visual Studio Code (VS Code) de una forma muy sencilla.

Algunas de las características y ventajas que se pueden destacar de REST Client son:

  • Ejecutar solicitudes HTTP y ver la respuesta directamente en el panel
  • Tener todas las solicitudes en un mismo fichero
  • Soporta los tipos de autenticación más comunes
  • Definición de variables de entorno
  • Generar code snippets

Voy a ir explicando paso a paso como empezar a utilizar esta herramienta.


Instalación

La instalación es muy sencilla puesto podemos acceder a la extensión desde el mismo VS Code:


Fichero

Una vez instalado REST Client, crearemos un fichero cuya extensión debe ser .http o .rest para que nuestro código sea compatible con las solicitudes HTTP que vayamos a realizar.


Entornos y variables

Hay diferentes formas de definir variables. Aquí vamos a explicar dos de ellas:

1. Variables de entornos
Se pueden definir variables de entorno en el propio settings de VS Code. En la siguiente imagen se muestra un ejemplo de cómo se configuraría un entorno para local y otro para producción.

Una vez configurado el fichero de settings, tenemos que seleccionar el entorno con el que queremos trabajar. Para ello abrimos nuestro fichero .rest, pulsamos F1, elegimos la opción: Rest Client: Switch Environment y seleccionamos el entorno.

2. Variables en el mismo fichero
Se pueden definir variables en el mismo fichero con la siguiente sintaxis:

@nombreVariable = valor

En la siguiente imagen se muestra un ejemplo con algunas variables y una llamada a un servicio GET haciendo uso de ellas.


Peticiones

REST Client nos permite realizar peticiones GET, POST, PUT y DELETE de una forma muy sencilla.

En la siguiente imagen vemos algunos ejemplos de peticiones, variables y de como se muestra el resultado de una petición.

Empezamos definiendo algunas variables como el host o el puerto la base de nuestro proyecto para poder reutilizarlas en todo nuestro código.

Seguimos escribiendo nuestras peticiones separadas entre ellas con un comentario que debe empezar por tres o más almohadillas (###) . Esto es importante ya que, sin ello, no podremos realizar las siguientes peticiones.

Después de cada una de estas líneas, tendremos el encabezado de nuestra solicitud, el cual podremos llamar situándonos encima de la petición y pulsando sobre Send Request

El resultado se mostrará en un panel que se abrirá a la derecha automáticamente.


Autorización

Además, como ya apuntaba al principio, Rest Client soporta los tipos de autenticación más comunes como: Basic Auth, Digest Auth, SSL Client Certificates. etc.

Aquí te muestro un ejemplo de cómo se añadiría una cabecera de «Authorization»:


Code Snippet

Una vez tengamos nuestras peticiones, podemos generar estas peticiones en el lenguaje que queramos.

Para poder generar este código nos situamos sobre la petición y con el botón derecho del ratón nos aparecerá un menú. Seleccionamos: «Generate Code Snippet»

Una vez seleccionado nos muestra un listado con los diferentes lenguajes y seleccionamos uno de ellos:

En mi caso selecciono JavaScript y automáticamente se abre un panel a la derecha con el código generado.

Esto nos puede ahorrar tiempo y ayudar bastante a la hora de realizar las llamadas a estas peticiones desde nuestra aplicación.

Como podemos ver, REST Cient nos permite realizar peticiones de una forma muy sencilla. Una de las cosas que más me gusta es que podemos tener todas nuestras peticiones de nuestro proyecto en el mismo directorio y hacer pruebas sin necesidad de abrir otros programas.

Si te interesa profundizar más, te recomiendo que sigas leyendo en la página de REST Client en GitHub.

Actualidad

25/05/2020

/ , , , ,

Recomendaciones para desarrollo de código seguro

Actualmente, tanto los clientes como los desarrolladores de software estamos muy preocupados por la calidad del código, pero parece que se sigue pesando más el cumplimiento de las fechas de entrega y el ajuste en el presupuesto.

Tan evidente es que en el documento de cumplimiento del servicio (ANS) que se firma en muchas colaboraciones se recogen todo tipo de variables, KPIs y sus correspondientes penalizaciones en el caso de incumplimiento, pero pocas veces se miden o se cuantifican criterios de seguridad.

En el mundo Agile en el que se definen las historias de usuario, se establecen los sprints y se hacen las fases más difusas y menos largas, en ningún momento se tiene en consideración la securización de las aplicaciones de forma explícita. Se diseña, construye, prueba y se entrega, pero ¿y la seguridad?, ¿en qué fase entra? ¿se considera incluida en la parte de construcción? ¿no debería ser una parte de cada una de las fases?

El diseño de aplicaciones debería abordarse siempre bajo criterios de seguridad, siguiendo unos estándares de construcción seguros y finalmente se debería probar la seguridad de cada una de las funcionalidades desarrolladas en el sprint correspondiente.

En este punto debemos reflexionar si en nuestro caso tenemos en cuenta la seguridad: ¿existe un checklist al respecto que validamos antes de entregar y/o recepcionar los desarrollos o mientras que cumplan con las funcionalidades definidas y se entregue en los plazos establecidos entendemos que es un buen trabajo?

No se necesita ser un experto ni tener un equipo de hacking ético para definir una línea base y poder testar la seguridad de nuestros desarrollos. Mi recomendación para definir correctamente esos mínimos debemos tomar como referencia OWASP – Open Web Application Security Project.

OWASP es una fundación sin ánimo de lucro cuyos miembros trabajan a favor de la seguridad del software en general. Cada año analizan los 10 principales riesgos que se producen en las aplicaciones.

El informe de 2020 nos recomienda tener en cuenta los siguientes aspectos:

1. Inyección – Injection

Consiste en la ejecución de comandos o querys directamente desde la propia aplicación. Supongamos que tenemos el típico método de acceso a una aplicación con los campos usuario y contraseña ¿Qué ocurriría si la consulta a la base de datos no tuviese mecanismos de validación o estuviese mal construida y permitiese meter en el campo usuario el siguiente texto?

' or "=';

La consulta siempre saldría positiva y accederíamos a la base de datos.

2. Pérdida de autenticación – Broken Authentication

Englobaría a todo lo referido en el tratamiento y técnicas de protección de credenciales y cómo las implementan las aplicaciones. Es decir, la utilización de canales no cifrados para transmitir credenciales (https), no proteger debidamente las credenciales de usuarios, mala gestión en la recuperación de credenciales, exposición de los ID de sesión (Cookies de sesión).
Un ejemplo de esto podría ser

xxxx.es/url/control?action=profile&ID=vH]OzN8F8_==

en el que el ID va directamente en la URL.

3. Exposición de datos sensibles – Sensitive Data Exposure

Se produce cuando un usuario puede comprometer los datos debido a una mala u obsoleta encriptación de los mismos. Tiene puntos de conexión con el reglamento general de protección de datos (RDPD). Un ejemplo claro puede ser guardar en un archivo de configuración el usuario y contraseña en “texto plano”.

4. Entidades externas XML – XML External Entities (XXE)

Antiguos procesadores de XML permiten la especificación de una entidad externa, una URI sin referencia y evaluada durante el procesamiento del XML. Es decir, que al parseador de XML se le envía un XML con funcionalidades añadidas. Ejemplo de esto podría ser:

]>&entidad;

5. Pérdida de control de acceso – Broken Access Control

El control de acceso determina qué usuarios se comunican con qué sistemas y recursos. Esto implica que los usuarios no pueden actuar fuera de los permisos previstos.

Un ejemplo de esto es la configuración incorrecta de CORS que permite el acceso no autorizado a una API, o acceder a una API sin control de acceso mediante el uso de POST, PUT o DELETE.

6. Errores de configuración de seguridad – Security Misconfiguration

En este apartado se incluiría todo aquello que se relaciona con el despliegue de una aplicación y su entorno. Es decir, permisos, configuraciones por defecto, actualizaciones librerías, sistema operativo, servidores de aplicaciones, puertos, servicios, cuentas de usuarios, manejo de errores, etc.

7. Cross-Site Scripting – Cross-Site Scripting (XSS)

Este ítem considera la posibilidad de modificar valores que la aplicación web usa para pasar variables entre dos páginas.Los ataques XSS se basan en la inyección de scripts maliciosos que se ejecutan en el navegador del usuario. En muchos casos, el usuario ni siquiera es consciente de lo que está ocurriendo. Los tipos principales de ataques XSS son:

  • Reflected: Normalmente se basan en conseguir que el usuario siga un link o complete un formulario malicioso, aunque aparentemente normal. (ejemplo: Phising)
  • Stored: El código malicioso se inyecta en algún elemento persistente, como una base de datos, de manera que se consigue que se ejecute en el navegador del cliente al recuperar la información almacenada.

8. Deserialización insegura – Insecure Deserialization

Estos defectos ocurren cuando una aplicación recibe objetos serializados dañinos y estos pueden ser manipulados por el atacante para realizar ataques de repetición, inyecciones o modificar sus privilegios de ejecución. En el peor de los casos, la deserialización insegura puede conducir a la ejecución remota de código en el servidor.

Los datos serializados (que el atacante envía al servidor de consumo de servicios) hacen que se ejecute, al momento de deserializar, un código arbitrario (malicioso).

Código con los datos originales:

a:4:{i:0;i:23;i:1;s:5:"Admin";i:2;s:4:"user";i:3;s:32:"fkg498ok32ñ5p0fw48";}

Código modificado en el que se le conceden permisos de administración a un usuario externo, siguiendo la misma serialización que el original

a:4:{i:0;i:12;i:1;s:7:"ITERIAM";i:2;s:5:"admin";i:3;s:32:"ufr53psm56115ld357";}

9. Utilización de componentes con vulnerabilidades – Using Components with Known Vulnerabilities

Este tipo de ataque ocurre cuando se emplean librerías o frameworks que contienen vulnerabilidades. Un ejemplo de esta vulnerabilidad es la no actualización de los plugins de WordPress.

10. Recolección de logs y monitorización insuficiente – Insufficient Logging Monitoring

No es en sí una vulnerabilidad de las aplicaciones, pero si hace que la detección y las acciones de mitigación sean más dificultosas y lleven más tiempo.

Con estos 10 puntos, podemos definir nuestra línea base de actuación en temas de seguridad, y a partir de ella, hacer un checklist e incorporarlo como tarea en nuestro desarrollo Agile.

¿Qué valor añadido le daría a un cliente el saber que el desarrollo que ha contratado o realizado, además de cumplir funcionalmente con las especificaciones, cumple unos mínimos de seguridad?

Y a partir de aquí, ¿cómo seguimos? Con nuestra línea base definida, con nuestros mínimos de seguridad implementados, ahora toca ir evolucionando y madurando de forma incremental como en cualquier otro sistema de calidad que tengamos en nuestra empresa.