Este post originalmente se publicó en la web de BBVA Next Technologies. No dudes en ver el original. No olvidéis seguir en las redes sociales a su coautora: Ruth González [Twitter][LinkedIn] ¡Es una referente! Y echadle un ojo al repo del lab de seguridad, ellos también se han volcado mucho en que este post salga TAN bien.
Las contraseñas son un sistema de autenticación en el que la concienciación y conocimiento del usuario son un factor clave. La usabilidad de este sistema ha demostrado no escalar bien cuando un usuario mantiene un gran número de servicios digitales, algo común en nuestro día a día. Como una evolución de estos sistemas apareció el concepto de passwordless, un sistema de autenticación más escalable, en cuanto a usabilidad, que se aprovecha de la gran adopción de dispositivos hardware como los smartphone, o en entornos especializados, del sensor de huella dactilar del ordenador portátil. En el artículo anterior se explicó el origen de passwordless, su evolución y el porqué de su aparición.
Este artículo se centrará en la parte más práctica de este tipo de sistemas de autenticación. Inicialmente se definirá a bajo nivel FIDO2. Como ya se mencionaba en el artículo anterior, este es un estándar abierto que se compone por dos especificaciones webauthn y CTAP2 que se definirán más adelante.
Para poder dar una visión a bajo nivel de cómo se debe implementarse un servidor que realice autenticación passwordless y mostrar en detalle el significado y utilidad de cada uno de los mensajes que maneja este protocolo, se ha desarrollado una aplicación web que nos servirá como expositor de cada uno de los conceptos necesarios para entender el protocolo. Todo el desarrollo se ha realizado mediante tecnologías y se ha liberado el código en el repositorio de GitHub del laboratorio de innovación en seguridad para que pueda complementarse la explicación expuesta en este artículo y comprobar de forma sencilla cómo se envían los mensajes aquí expuestos.
El siguiente vídeo muestra la demo de la aplicación así como los mensajes y datos que se intercambian cliente y servidor en el proceso de autenticación.
Existen muchas empresas que ofrecen soluciones de autenticación passwordless basadas en FIDO. También existen multitud de proyectos Open Source que recogen tanto servidores FIDO como librerías y SDKs para incorporar en el cliente, ya sea un navegador o una aplicación móvil. Un ejemplo interesante a tener en cuenta en el ámbito empresarial sería NokNok que ofrece una plataforma completa de autenticación sin contraseñas que incluye la gestión de usuarios así como un conjunto de SDKs para crear aplicaciones en dispositivos móviles. También existen alternativas, en el ámbito Open Source, donde destacan Duo Labs o Yubico. Ambas cuentan con alternativas de servidor desarrolladas en distintos lenguajes de programación.
Como parte de este artículo, hemos querido desarrollar una pequeña aplicación a modo de prueba de concepto para poder comprobar el funcionamiento de esta tecnología. La aplicación se estructura en dos partes. La primera es la de registro del usuario en el servidor y, la segunda es la de autenticación del usuario previamente registrado.
En FIDO2 existen tres elementos implicados cuya función debe quedar clara para poder entender el funcionamiento de la especificación:
Es el encargado de realizar las validaciones. Debe gestionar y almacenar las claves públicas de los usuarios. Contiene la mayor parte de la lógica. Existen servidores certificados que garantizan la interoperabilidad de los sistemas y que ya incorporan todas las funciones necesarias del estándar en cuanto a codificaciones, funciones criptográficas, validaciones y estructura de los mensajes a enviar y a recibir por el navegador. La mayoría de estos servidores certificados son de pago aunque existe alguno como StrongKey, un servidor FIDO escrito en Java, que es Open Source. Existen alternativas no certificadas en diferentes lenguajes de programación. A pesar de no estar certificados se pueden emplear de la misma manera aunque no garantizan la compatibilidad. El servidor se comunica con la aplicación javascript que corre en el navegador. La especificación que define el conjunto de mensajes y el formato de los mismos que deben intercambiar servidor y cliente, entendiendo como cliente el navegador, se denomina webauthn.
Es el elemento que utiliza el cliente para verificar su identidad. Al igual que en el caso de los servidores, las empresas que proporcionan estos dispositivos o funcionalidades se pueden certificar en el estándar. Puede ser una llave física o un dispositivo con algún sistema biométrico como la huella dactilar.
Es el encargado de ejecutar la aplicación javascript. Se encarga de comunicarse con el authenticator. Emplea la especificación CTAP2 (Client To Authenticator Protocol 2) para comunicarse con el authenticator.
La prueba de concepto se ha desarrollado usando Vue.js para implementar el frontal web. Para el backend, hemos buscado una librería que cumpla el estándar de la FIDO Alliance. Es por eso que hemos optado por este módulo de Python que implementa todas las funciones definidas en el estándar.
En el ejemplo, se utiliza un Macbook Pro con sensor de huella dactilar que hace la función de authenticator. La aplicación desarrollada está dockerizada y puede desplegarse en cualquier SO. Para poder probarla es necesario contar con una llave externa o con un portátil o dispositivo con sistemas biométricos compatibles con FIDO2.
El código de la aplicación está disponible en GitHub. El repositorio se divide en dos carpetas front y back. En la carpeta front se encuentra el código del navegador mientras que en back está el servidor FIDO. Dentro de esta última carpeta hay que destacar la carpeta trusted_attestation_roots que incorpora los certificados raíz de los authenticators permitidos. En el caso de usar uno distinto a los encontrados en esta carpeta será necesario incorporar el certificado raíz.
En la aplicación desarrollada nos centraremos en la especificación webauth que como se ha comentado es la que define el conjunto de mensajes entre servidor y cliente.
La comunicación entre el navegador y el authenticator será transparente para el usuario y son el propio navegador y el authenticator seleccionado los encargados de gestionar y enviar los mensajes.
A la hora de registrarse mediante FIDO, el usuario debe proveer una serie de datos iniciales en un formulario web como si de un registro tradicional se tratara, a excepción de la contraseña que en este caso no será necesaria. Los datos solicitados para el registro serán definidos por la aplicación que implementa el sistema de autenticación. Estos datos no formarán parte del proceso de autenticación, ya que FIDO se basa en criptografía de clave asimétrica, sin embargo, serán almacenados por la aplicación web para generar la sesión del usuario tras el proceso de autenticación.
El usuario introduce sus datos en en el formulario de registro y la aplicación JavaScript del navegador los envía al servidor. Este mensaje no entra dentro del alcance propuesto por el estándar por lo que el formato puede variar. En este ejemplo se envía una petición POST con los datos en formato JSON.
El servidor recoge los datos del cliente, crea un challenge de bytes aleatorio, de al menos 16 bytes, con el fin de evitar ataques de replay. Además, se añaden ciertos datos del propio servidor e información de los algoritmos deseados para la creación de las claves. Con todos esos datos, el servidor crea el objeto PublicKeyCredentialCreationOptions, que es devuelto al navegador.
El protocolo para la transmisión de todos los mensajes no está definido en la especificación, pero al igual que en el paso anterior, y los posteriores, se realiza mediante una conexión HTTP. Al ser una prueba de concepto, en nuestro caso, hemos realizado el paso de mensajes sobre una conexión HTTP (sin cifrar) para que sea sencillo de desplegar y puedan verse los mensajes que se envían fácilmente, aunque, en un entorno productivo la comunicación debería realizarse sobre HTTPS.
El navegador llama a la función authenticatorMakeCredential() del authenticator. Internamente el navegador valida los parámetros y rellena algunos por defecto. Los parámetros de la llamada create() se le pasan al authenticator junto con un hash SHA-256 del objeto clientDataJSON. Al método solo se le pasa el hash del objeto para no sobrecargar conexiones de baja velocidad y garantizar que no se ha alterado la información.
El authenticator necesita verificar la identidad del usuario para ello le solicita que introduzca o bien su huella dactilar o su llave física externa. Una vez todo es correcto genera un par de claves pública y privada.
La clave privada se almacena, en el servidor, asociada a un Credential ID y la clave pública se devuelve al navegador junto con información del cifrado.
La aplicación JavaScript genera el objeto AuthenticatorAttestationResponse y se lo devuelve al servidor. Este objeto contiene:
El servidor debe verificar los datos, tiene que comprobar que el challenge es idéntico al enviado al navegador en el Paso 1. También se debe comprobar que la firma del authenticator es válida y para ello debe de seguir la cadena de confianza de certificados correspondiente. Cuando todas las validaciones son correctas, el servidor almacena la clave pública y el Credential ID con los datos del usuario y el registro finaliza correctamente.
La prueba de concepto se ha realizado en un dispositivo Mac con huella dactilar incorporada, es por eso que ha sido necesario incluir el certificado root de Apple para que el servidor FIDO sea capaz de verificar el origen del authenticator del Mac. Si se usa otro dispositivo, ya sea llave externa o un sistema embebido en un PC habría que añadir el certificado a la carpeta correspondiente.
El usuario se ha registrado en la aplicación mediante el proceso anterior. A partir de ahora, podrá identificarse en la aplicación mediante un sencillo proceso en el que entra en juego el uso de la biometría o la posesión de un token hardware. En este caso, el usuario entrará en la aplicación. La aplicación le requerirá que se identifique. Entonces el usuario seleccionará identificarse mediante FIDO y empezará el proceso de autenticación.
El cliente introduce sus datos en el formulario de login y estos se envían al servidor FIDO.
El servidor genera el objeto PublivKeyCredentialRequestOptions que contiene un challenge aleatorio y el CredentialID asociado al usuario.
El navegador completa automáticamente algunos de los campos y llama a la función get() del authenticator.
El authenticator solicita que el usuario verifique su identidad introduciendo su huella o su llave física. Si todo es correcto genera una firma usando la clave privada del usuario y se la devuelve al navegador junto con los datos que ha empleado para generar dicha firma.
El authenticator devuelve los datos al navegador.
El navegador crea y envía el objeto AuthenticatorAssertionResponse con la firma, el clienDataJson con datos del usuario y el authenticatorData que contiene información necesaria para que el servidor pueda comprobar la firma.
El servidor valida la firma, para ello la descifra con la clave pública del cliente. Después calcula un hash SHA-256 del objeto clientDataJSON y lo concatena con el valor en bytes del objeto AuthenticatorData, si el resultado es igual al de la firma descifrada, se da por válido. Esta no es la única comprobación que hace el servidor aunque sí la más importante. Si alguna de las comprobaciones de la especificación falla, la autenticación se invalida, en caso contrario sería correcta y se le devolvería al navegador algún mecanismo para mantener la sesión. Este mecanismo está fuera del alcance de la especificación. Una opción para esto podría ser el uso de un token JWT.
FIDO 2 es un estándar con un alto nivel de madurez que permite autenticación sin contraseñas o el uso de múltiples mecanismos de autenticación como un segundo factor a través de dispositivos externos como pueden ser las llaves físicas, los dispositivos móviles o sistemas embebidos en los propios ordenadores portátiles.
Con la definición de las distintas especificaciones un desarrollador puede incorporar fácilmente estos mecanismos de autenticación.
A pesar de la transparencia brindada por las librerías FIDO2, para poder incorporar este estándar a nuestros servicios tradicionales es esencial conocer cuáles son las funciones del servidor FIDO2, entender cada una de las comprobaciones que dicho servidor debe de hacer y el por qué de las mismas. En los sistemas de autenticación passwordless como este, es imprescindible el gestionar de forma adecuada las claves públicas de los usuarios, necesarias para descifrar los mensajes durante el proceso de autenticación. Una mala gestión de estas claves podría provocar el que el usuario no pudiese autenticarse o que un atacante pudiese impersonar a un usuario de ser capaz de modificar esa clave pública.
Como se ha demostrado, existen diferentes servidores FIDO open source, certificados por la FIDO Alliance, que se pueden utilizar e incorporar en proyectos desarrollados en una gran variedad de lenguajes de programación así como librerías para integrar esta solución también en el lado del cliente.
Si por el contrario se quiere una solución más adaptada a un caso de negocio concreto o que cuente con servicio de soporte, se puede hacer uso de los diferentes productos comerciales de las empresas del sector, estas ofrecen diferentes alternativas y soluciones para la implementación e integración de soluciones passwordless que convivan o se superpongan a otras más tradicionales ya presentes en los sistemas de la organización.
Fuente Imagen destacada: unsplash