Implementar un JWT (JSON Web Token) Authorization Server en web2py

Implementar un JWT (JSON Web Token) Authorization Server en web2py

Objetivo

Implementar un Authorization Server en web2py para generar tokens válidos con el estándar JSON Web Token (JWT) para controlar el acceso a APIS REST expuestas en NondeJS, adicionalmente esto nos permitirá liberar al servidor de la gestión de sesiones realizando peticiones state less.

Desarrollo

El control de accesos utilizado actualmente por el Framework web2py es Control de Acceso Basado en Roles (RBAC), el cual es una técnica para restringir el acceso al sistema a usuarios autorizados a través de roles, permitiendo asignar los privilegios mínimos necesarios a dichos usuarios, por lo que se pueden aprovechar estas funcionalidades de control de accesos para generar tokens JWT de manera segura basado en los roles.

El control de accesos nativo en web2py nos permitira configurar “claims” personalizados y basado en roles para restringir el acceso y ejecución de procesos y otras acciones en APIs.

Aunque en este ejemplo se definen “claims” considerando solo el usuario y rol asignado, se pueden definir más parámetros y ámbitos para definir los tipos de recursos protegidos a los que el cliente puede acceder.

Para conocer más de la estructura de JWT puede visitar https://jwt.io/introduction/

Para conocer más sobre los claims utilizados por JWT puede visitar https://www.iana.org/assignments/jwt/jwt.xhtml

A continuación se presenta la arquitectura propuesta.

enter image description here

web2py soporta distintos tipos de autenticación como LDAP, OpenID, PAM, OAuth2.0, entre otros, pero en este caso utilizaremos una autenticación básica HTTP el cual es un mecanismo sencillo de autenticación, donde las credenciales se envían dentro del Header HTTP Authorization codificadas en base64 de la forma username:password.

GET /index.html HTTP/1.0
Host: basic.example.com
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

El servidor responde con un 200 OK si el usuario es autenticado de manera exitosa o de otra manera con un código 40X.

Implementación del Authorization Server

Utilizaremos la estructura de Control de Acceso Basada en Roles (RBAC) implementada en el framework web2py python, dicho control está basado en la siguiente estructura.

  • auth_user almacena el nombre del usuario, dirección de correo electrónico, contraseña y estado (pendiente de registro, aceptado, bloqueado)
  • auth_group almacena los grupos o roles para usuarios en una estructura muchos-a-muchos. Por defecto, cada usuario pertenece a su propio grupo, pero un usuario puede estar incluido en múltiples grupos, y cada grupo contener múltiples usuarios. Un grupo es identificado por su rol y descripción.
  • auth_membership enlaza usuarios con grupos en una estructura muchos-a-muchos.
  • auth_permission enlaza grupos con permisos. Un permiso se identifica por un nombre y opcionalmente, una tabla y un registro. Por ejemplo, los miembros de cierto grupo pueden tener permisos "update" (de actualización) para un registro específico de una tabla determinada.
  • auth_event registra los cambios en las otras tablas y el acceso otorgado a través de CRUD a objetos controlados con RBAC.
  • auth_cas se usa para el Servicio Central de Autenticación (CAS). Cada aplicación web2py es un proveedor de CAS y puede opcionalmente consumir el servicio CAS.

enter image description here

Para habilitar autenticación básica se debe importar la siguiente clase en el model de web2py.

from gluon.contrib.login_methods.basic_auth import basic_auth

Y habilitar las siguientes configuraciones en el model auth policy

auth.settings.allow_basic_login = True              #activate basic auth
auth.settings.login_methods = [basic_auth()]   #force to use only basic auth

Una vez realizadas las configuraciones anteriores en el model, se define la siguiente función en el controller, dicha función será utilizada para enviar los tokens a los usuarios autenticados exitosamente.

@auth.requires_login()
def api_get_jwt():
    session.forget()
    if not request.env.request_method == 'GET': raise HTTP(403)
    import jwt
    import json
    import datetime
    secret = 'secret'
    role = db((db.auth_membership.id==auth.user.id) & (db.auth_membership.group_id==db.auth_group.id)).select(db.auth_group.role).first().role
    sub = auth.user.username
    exp = datetime.datetime.utcnow() + datetime.timedelta(seconds=60)
    payload = {'sub':sub, 'role': role, 'exp': exp}
    encoded_jwt = jwt.encode(payload, secret, algorithm='HS256' )
    return encoded_jwt

Algunos puntos a resaltar son:

  • Se utilizan las librerias python pyjwt (pip install pyjwt).
  • La variable “secret” se utiliza para firmar el token.
  • El rol del usuario se obtiene consultando la tabla auth_membership.
  • Se define un tiempo de expiración del password en la variable exp.
  • Las peticiones se reciben por HTTP GET pero se puede indicar otro método HTTP.

Lo anterior expone una API que es accedida solo por usuarios previamente registrados y autenticados por medio de HTTP Basic Auth, lo cual se muestra con la siguiente petición.

enter image description here

Una vez obtenido el token JWT podemos realizar la petición de recursos a través de APIs expuestas en un entorno de ejecución NodeJS express, el cual validará la correcta estructura y firma del token recibido, así como el rol asignado al usuario, para permitir el acceso solo a las APIs autorizadas.

Para realizar este ejemplo definimos la siguiente función, la cual será accedida solo por usuarios con un token válido.

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const router = express.Router();

router.post('/protected', function(req, res)
        {
                var token = req.headers['authorization']
                if(!token){
                        res.status(401).send({error: "No token found"})
                        return}

                token = token.replace('Bearer ', '')
                var decoded = jwt.decode(token);

                jwt.verify(token, 'secret', function(err, user)
                        {
                            if (err)
                            {
                                res.status(401).send({error: 'Invalid Token'})
                            } 
                            else if (decoded['role'] == 'admin')
                            {
                                    res.send({message: 'Authorized'});
                            }
                            else
                            {
                                res.send({message: 'NOT Authorized'})
                            }
                        }
                )
        }
);

Algunos puntos a resaltar son:

  • Se utiliza la libreria 'jsonwebtoken' y ‘express’.
  • En la función “verify” se requiere de la llave utilizada para firmar el token jwt.verify(token, 'secret', function(err, user).
  • Con la variable decoded obtenemos los claims decodificados para validar el rol, en este caso solo permite acceso a usuarios con el rol “admin”.

enter image description here

Conclusiones

Implementando un Authorization Server para obtener tokens JWT en web2py es una buena opción ya que se aprovecha la estructura definida nativamente de control de accesos basada en roles RBAC, lo cual nos permite definir "claims" y restringir el acceso a APIs de manera segura.

Levantamiento de Información (Information Gathering)

Una de las primeras fases que se deben realizar en el proceso de pruebas de seguridad en una aplicación Web es el levantamiento de información, ya que esta fase nos ayudará a entender la aplicación desde su contexto (tipos de datos que almacena/procesa/transmite, procesos que soporta, tipo de usuarios), hasta las versiones de infraestructura, frameworks y lenguajes de desarrollo utilizados.

Esta información nos ayudará a perfilar el sistema y diseñar pruebas de seguridad y ataques específicos, para que nuestras pruebas sean más efectivas.

Las fases para el levantamiento de información son las siguientes:


  • Conduct search engine discovery/reconnaissance for information leakage (OTG-INFO-001)
    • Existe elementos directos e indirectos que pueden ser identificados.
    • Los elementos directos están relacionados con el contenido de la aplicación.
    • Los elementos indirectos relacionados con la información sensible de diseño y configuración mediante búsquedas en foros, grupos de noticias y sitios web de licitación (sitios de terceros).
    • El objetivo es identificar información sensible expuesta sobre el sistema/aplicación/organización en elementos directos o indirectos.
    • Herramientas: shodan, google hacking.

  • Fingerprint Web Server (OTG-INFO-002)

    • Identificar la versión de web server sobre la que se ejecuta la aplicación permite identificar posibles vulnerabilidades y ejecutar pruebas dirigidas y exploits específicos.
    • Para identificar la versión utilizada se debe analizar la respuesta del servidor.
    • Herramientas: Netcat, envío de peticiones malformadas, nikto.

  • Review Webserver Metafiles for Information Leakage (OTG-INFO-003)

    • Identificar información sensible expuesta en directorios y archivos.
    • robots.txt.
    • Herramientas: FOCA (Metadata extraction, Network analysis, DNS snooping, Search for common files, Juicy (interesting) file search, Proxies search, Technology identification, Fingerprinting, Leak detection, Backups search, Error forcing, Open directories search, Vulnerability detection).

  • Enumerate Applications on Webserver (OTG-INFO-004)

    • Identificar las aplicaciones que están hosteadas en el servidor.
    • Se pueden encontrar diferentes aplicaciones que apuntan a la misma IP, por lo cual es importante definir el alcance de las pruebas.
    • También es importante identificar las URLs que pueden estar asociadas a diferentes aplicaciones.
    • El objetivo es enumerar todas las aplicaciones que existen en el Webserver.
    • URL base diferente.
    • Puertos no estándar.
    • Virtual hosts permite hacer uso de un servidor para alojar múltiples dominios o sitios en una única interfaz o IP.
    • El protocolo DNS permite que una sola IP sea asociada con uno o más nombres simbólicos.
    • Visualizar directorios debido a configuraciones inseguras.
    • Directorios o aplicaciones referenciados por otras aplicaciones.
    • Fuerza bruta para obtener directorios.
    • Herramientas: Nmap, Nikto, Transferencia de zona DNS.

  • Review webpage comments and metadata for information leakage (OTG-INFO-005)

    • Los comentarios y metadata en recursos públicos como archivos html, js, y otros puede revelar información sensible (lógica de la aplicación, código, IPs, usuario/password).
    • El objetivo es entender el funcionamiento de la aplicación e identificar información sensible expuesta.
    • Herramientas: wget, requests python, HTTP proxy.

  • Identify application entry points (OTG-INFO-006)

    • Identificar datos de entrada como posibles vectores de ataque a la aplicación.
    • Analizar las peticiones y respuestas HTTP que requieren ser investigadas más a detalle.
    • Identificar información sensible que es se expone en las peticiones/respuestas HTTP.
    • Identificar cookies, parámetros, redirecciones, mensajes HTTP, headers que se utilizan como datos de entrada a la aplicación.

  • Map execution paths through application (OTG-INFO-007)

    • Entender la estructura de la aplicación, e identificar los principales flujos de datos.
    • Es importante definir el alcance de los flujos que se van a probar ya que en la práctica resulta complicado revisar el código completo de una aplicación.
    • Se pueden tomar los datos de entrada de los usuarios para identificar el flujo y transformación de datos utilizada en la aplicación.
    • También se recomienda validar con el dueño de la aplicación sobre las funcionalidades y las secciones de código que está más interesado en validar.
    • En pruebas de caja negra se pueden utilizar herramientas y técnicas de “spidering” para identificar LinksURLs.
    • En pruebas de caja blanca se puede hacer uso de la documentación de la aplicación y el código para definir los flujos que se van a probar.

  • Fingerprint Web Application Framework (OTG-INFO-008)

    • Conocer el framework sobre el que fue desarrollado la aplicación nos ayuda a identificar posibles vulnerabilidades, configuraciones inseguras específicas, y la estructura de archivos utilizada.
    • Se puede identificar el framework utilizado en los headers, mensajes HTTP, cookies, código HTML, archivos y directorios específicos (Ej. X-Powered-By, X-Generator).
    • Cada framework tiene una estructura de directorios y archivos específico, que debe ser analizada, en algunos casos es necesario instalar localmente el framework para analizar su estructura.

  • Fingerprint Web Application (OTG-INFO-009)

  • Map Application Architecture (OTG-INFO-010)

    • Identificar los elementos y componentes que conforman la arquitectura de la aplicación (firewall, balanceadores, DMZ, reverse proxy, front-end server, LDAP server).

SQL Injection (I)

SQL Injection (SQLi) es una vulnerabilidad que afecta las aplicaciones y servicios Web que hacen uso de servicios de Bases de Datos (consultas, creación/modificación de registros) a través de datos de entrada (parámetros, headers, cookies) que son incluidos o utilizados para ejecutar sentencias o comandos, los cuales se pueden manipular para ejecutar acciones no autorizadas sobre la Base de Datos.

De acuerdo a OWASP Top 10 2017, SQL Injection ocupa el primer lugar dentro de las vulnerabilidades que afectan las aplicaciones y servicios Web, por lo que es importante su identificación, así como el diseño de controles para prevenir su explotación.

El impacto que puede ocasionar este ataque depende principalmente del tipo de dato que almacena la Base de Datos (datos personales, financieros, privados), así como los privilegios que tiene el usuario que utiliza la aplicación para ejecutar acciones sobre la Base de Datos, los cuales van a permitir acceder a diferentes recursos del sistema.

En un ataque exitoso esta vulnerabilidad permite ejecutar sentencias y comandos de manera no autorizada sobre la Base de Datos, diferentes a las que estaban previstas por el programador del sistema.

Aunque actualmente existen diferentes arquitecturas Web (MVC, REST, etc.) podríamos definir los siguientes componentes en una aplicación Web.

enter image description here

El ataque de SQLi se ejecuta desde la capa de aplicación, por lo que si no existen controles de seguridad en las distintas capas del sistema, un atacante podrá ejecutar acciones no autorizadas sobre los recursos de la Base de Datos.

El proceso general para identificar este tipo de vulnerabilidades es el siguiente:

enter image description here

Seguridad Aplicaciones y Servicios Web (Introducción)

Introducción

El creciente uso de aplicaciones y servicios Web para soportar negocios, redes sociales, servicios financieros, hacen que las vulnerabilidades en esta capa impacten de manera significativa a los usuarios, negocios y sociedad.

Adicionalmente el uso de nuevas tecnologías (IoT, microservicios, cloud, etc) y de metodologías ágiles, hace que la mitigación de riesgos sea más compleja.

Por lo anterior es importante considerar controles de seguridad en todo el ciclo del desarrollo del software para minimizar los riesgos. La validación e implementación de controles de seguridad desde las fases iniciales del ciclo de desarrollo del software hace más eficiente y menos costosa la corrección de errores y vulnerabilidades.

La capa de aplicación regularmente es la más expuesta, ya que por naturaleza está abierta a diferentes usuarios y aplicaciones que acceden para obtener recursos, es por ello que esta capa representa un vector de ataque importante.

75% de los ataques ocurren en la capa de aplicación.

Por otro lado regularmente los programadores están más enfocados al desarrollo de las funcionalidades del sistema y que este cumpla con los requerimientos establecidos, por lo que no se consideran mejores prácticas de seguridad en el ciclo de desarrollo, lo que al final provoca que la aplicación exponga vulnerabilidades o huecos de seguridad en producción que pueden ser explotados por atacantes, impactando los procesos que soporta la aplicación, y la información que se almacena, procesa o transmite. Adicionalmente las aplicaciones están en constante actualización para poder soportar los objetivos del negocio, y utilizando metodologías ágiles o DevOp, por lo que la validación de controles y seguridad también se debe ajustar a estas necesidades.

Por lo anterior es importante considerar al menos los siguientes factores, para mitigar los riesgos de seguridad en el desarrollo de aplicaciones y servicios Web:

  • Tecnología.
  • Personas.
  • Procesos.