Introducción
Pues recientemente me ha llegado la Yubikey que pedí, y tras un tiempo usándola me ha dado por investigar y conocer exactamente como funciona por dentro. Tras hacer pruebas y leerme la documentación oficial, creo que (opinión personal), el producto no es tan bueno como pensaba en un inicio.
Os voy a explicar como funciona por dentro, qué ocurre exactamente cuando pulsamos el botón de nuestra Yubikey, si es un sistema realmente seguro, etc... así cada uno podrá sacar sus conclusiones.
Qué es una Yubikey?
Yubico, la empresa detrás de Yubikey, la autodenomina como una "llave de seguridad", que permite añadir una capa adicional a la hora de autenticar un servicio. Digamos que es un complemento adicional al clásico usuario/password que todos conocemos.
En esencia, una Yubikey es un OTP (One Time Password): un dispositivo que genera una contraseña de un solo uso. De esta manera cualquier servicio de autenticación te pedirá 2 datos para acceder (el password de tu usuario y el OTP). El uso de OTPs es algo conocido en el mundo de la seguridad informática y son un excelente sistema siempre y cuando se implementen correctamente. Principalmente existen 2 algoritmos de generación de OTPs:
- HTOP: Basado en un contador
- TOTP: Basado en un reloj
Para entender cómo funciona el OTP de Yubico es bueno que primero entendamos cómo funciona un OTP estándar como HTOP o TOP, así que vamos a ello.
HTOP - El contador que da seguridad.
Este algoritmo necesita 2 datos de entrada para generar un OTP. El primer dato es una clave secreta que solo conoce la aplicación/servicio que te va a autenticar (instagram, twitter, amazon...) y el usuario final de la cuenta (tú). Algunos servicios permiten que el usuario establezca esta clave secreta, aunque no es habitual. Normalmente la aplicación generará la clave secreta sin que el usuario la conozca, en forma de QR.
El segundo dato se trata de un valor que cambia en cada uso, en este caso un contador se va incrementando cada vez que generamos un nuevo OTP.
Con estos 2 datos, el algoritmo realiza una serie de operaciones matemáticas y devuelve un resultado. Para no complicar este thread, voy a inventarme un algoritmo muy tonto y simple para que todos podamos entender la esencia de esta operación matemática: cogemos la primera y última letra del "secreto", lo traducimos a número y lo multiplicamos por el contador.
Imaginemos que nuestro secreto para el HOTP es "derroido" y que todavía no hemos generado ningún código, así que el contador es 1. El resultado de generar 3 HOTP con este algoritmo y estos datos sería:
- Secreto: "derroido", primera letra d, última o. Traducimos a número (a = 1, b = 2, c = 3, d = 4, ...) 4 y 16
- Combinación con el contador: 416 * 1 = 416
Nuestro primer HOTP sería 416, el segundo 832, el tercero 1248, etc...
De esta manera, un atacante necesitaría conocer la palabra secreta y el contador para generar el siguiente OTP válido, haciendo que este algoritmo sea seguro, ya que el secreto jamás se escribe en la aplicación para hacer login. Quedaría de la siguiente forma:
Os dejo esta web que permite generar HOTP válidos a partir de una semilla y un contador. En esta web utilizan el algoritmo real (no este que he inventado yo y que no sirve de nada xD): https://asecuritysite.com/hash/hotp
Si establecemos como seed/secreto la palabra derroido, el OTP para el contador 0 será: 327793, 1 = 479774, 2 = 739258, ...
En el momento de hacer login en una web, la aplicación (por ejemplo twitter), rescata de su base de datos el secreto que te asignó y el contador almacenado (58 por ejemplo). El usuario en el momento de hacer login, generará un OTP usando el secreto que tienes con twitter (ambos secretos deben coincidir) y el contador actual (que deberá ser mayor que 58), generando así un nuevo OTP que twitter tomará como válido.
Ahora ya entiendes como funciona HOTP, pasemos a su hermano mayor: TOTP
TOPT - El reloj que da seguridad.
Este algoritmo es una copia exacta a HOTP. Necesita 2 datos de entrada para generar un OTP.
El primer dato es una clave secreta que solo conoce la aplicación/servicio que te va a autenticar (instagram, twitter, amazon...) y el usuario final de la cuenta (tú). Es normal que dicha clave se presente al usuario en formato de código QR.
El segundo dato ha de ser un valor que cambia en cada uso, en este caso el tiempo actual (un reloj). Aquí ya no usamos contadores.
De nuevo, el agoritmo realizará unas operaciones matemáticas con estos 2 datos de entrada. No voy a explicar dichas operaciones, pero volveré a aplicar mi algoritmo inventado inútil que creo que se entiende bien.
Nuestro secreto de nuevo es "derroido", y el algoritmo cogerá la primera y última letra, traduciendo a su valor número en función de la posición del abecedario. Luego le añadimos la hora actual al final. Quedaría así:
Secreto: derroido, cogemos la D y la O, cuyos números son 4 y 16
La hora actual es 18:05 y 30 segundos, así que el OTP en ese instante es: 416180530
Para el caso de TOPT, se realiza una última operación que permite que los resultados sean válidos durante 30 segundos. Eso se consigue dividiendo el valor entre 30 y eliminando la parte decimal. Si no se hiciera eso, cada OTP generado por TOTP solo sería válido dentro del mismo segundo que se ha generado, siendo imposible su uso doméstico.
Si repetimos este ejemplo a las 19:00:00, el OTP sería 416190000, así sucesivamente. El esquema sería el siguiente:
Os dejo un par de webs para que podáis jugar y testear los TOPT.
https://authenticationtest.com/totpChallenge/ Se genera un secreto y se os presenta un formulario para hacer login. Para generar un TOTP válido podéis usar: https://totp.danhersam.com/ usando el secreto random de la primera web.
Cual es mejor? HOTP vs TOTP
Ambos algoritmos son en esencia lo mismo. Usan las mismas fórmulas para calcular el OTP. Ambos necesitan de un valor secreto estático y un valor dinámico. En uno es un contador y en el otro es el tiempo.
Al parecer, internet se ha decantado por TOPT, ya que todos los servicios que conocemos lo han implementado. TOPT tiene la ventaja de que un OTP válido quedará invalidado tras 30 segundos, ofreciendo una capa de seguridad importante en el caso de que un atacante consiga capturar o hacer una foto de tu generador de códigos... dicho código solo será validad durante 30 segundos, haciendo que sea complicado que lo lleguen a utilizar en un lapso de tiempo tan corto.
Un código HTOP válido, será válido hasta que se genere (y se incremente de nuevo el contador) el siguiente código. Un atacante que tenga acceso físico a tu generador de OTP, podría generar un OTP mientras duermes y utilizarlo horas después para hacer login. Con TOTP esto no lo podría hacer transcurridos 30 segundos. Esta es la principal razón por la que la comunidad prefiere TOTP antes que HOTP.
Volvamos ahora a Yubikey...
Qué es Yubikey? HTOP? TOTP?
Pues ni una cosa ni la otra. Una vez entiendes cómo funciona Yubikey por dentro, te das cuenta que no es TAN bonito como creías.
Es cierto que si tengo que clasificar la Yubikey en uno de estos 2 sistemas, el que más se le parece es HOTP. Pero lo han implementado de forma diferente y lo han hecho dependiente (y vulnerable) a los servidores oficiales de Yubico. Y esta es precisamente la parte que no me gusta nada de este sistema.
Exacto, cuando usamos nuestra Yubikey, los servidores oficiales de Yubico entran en el proceso de validación. Es un sistema dependiente de estos servidores y si el día de mañana dejan de existir, la Yubikey no funcionará. También existe el (improbable) riesgo de que Yubico sufra una brecha de seguridad y las claves privadas queden expuestas, vulnerando e invalidadno todas las Yubikeys del mercado. Por último, una Yubikey tiene un número limitado de usos. Tras superar ese límite, la Yubikey deja de funcionar.
Pero bueno, para entender esto, vamos a explicar paso por paso cómo funciona una Yubikey, por qué necesita contactar con los servidores de Yubico, que es eso de la clave privada, etc....
El algoritmo de Yubikey explicado
Lo primero que me chocó de la Yubikey es que en el momento que decides emparejarla con una aplicación o servicio, no se genera ningún secreto como en HOTP o TOTP. Sin una clave secreta compartida entre el usuario y la aplicación... ¿cómo demonios funciona el sistema? Si pulsamos el botón de nuestra Yubikey se genera un código de letras, en mi caso el siguiente:
cccccbdtlrntnjhllegehedridbnrubrnfrfhvhckgef
Si lo vuelvo a pulsar, tras unos segundos, obtengo:
cccccbdtlrntuncbncjcivlilfubudtlubenkikjcjhk
Y de nuevo, si lo pulso:
cccccbdtlrntnhnejctjrhnhktdvebrntnhefingnkvb
Está claro que cada uso de mi Yubikey genera un código distinto. Y sabiendo que no accede a ningún reloj, cada generación de código nuevo se tiene que combinar con un contador, forzosamente. Otro dato interesante es que siempre se repite un patrón estático de letras, las iniciales: cccccbdtlrnt
. Vamos a destripar y entender el significado de estas letras...
Este código de 44 letras se divide en 2 partes:
- Los primeros 12 carácteres: ID pública de la Yubikey
- Los 32 carácteres restantes: Secuencia determinada por valores estáticos y dinámicos para añadir entropía.
Cogiendo el primer ejemplo: cccccbdtlrntnjhllegehedridbnrubrnfrfhvhckgef
, los 12 primeros caracteres son cccccbdtlrnt
y se corresponden a mi ID público. Éste ID público es estático y sirve para identificar al usuario con la aplicación. De esta manera, por ejemplo Twitter, sabe que el usuario "aikoncwd" tiene la Yubikey asociada cccccbdtlrnt
. Cualquier OTP introducido para validad al usuario "aikoncwd" deberá empezar por la ID de Yubikey que se utilizó cuando se configuró la cuenta (cccccbdtlrnt
).
Ahora toca validar los 32 carácteres restantes (el propio OTP). Primero hay que reversar la codificación ModHex que utiliza Yubikey. La llave USB es en realidad un teclado, que al pulsar el botón, escribe las letras del código. Para evitar incompatibilidad con los diferentes layouts/idioma del teclado, Yubikey utiliza una codificación para transformar un número hexadecimal (0-9 y a-f) a solo letras.
En mi caso, la secuencia njhllegehedridbnrubrnfrfhvhckgef
quedaría convertida en b86aa353632c721bce1cb4c46f609534
Éste código de 16 bytes hexadecimal se separa de la siguiente forma: b86aa353632c 721b ce1cb4 c4 6f60 9534
- 6 bytes:
b86aa353632c
ID Privado
- 2 bytes:
721b
Contador uso
- 3 bytes:
ce1cb4
Timestamp
- 1 byte:
c4
Contador uso sesión
- 2 bytes:
6f60
Random
- 2 bytes:
9534
Checksum CRC16
ID Privado
Los primeros 6 bytes corresponden al ID privado de mi Yubikey. Éste ID es secreto y lo asigna Yubico en el momento que fabrica y programa la Yubikey. Seis bytes soportan hasta 281.474.976.710.656 combinaciones únicas, que sería el total de Yubikeys diferentes que se podrían fabricar en total. Éste número supera la población mundial miles de veces, así que no hay problema de que lleguemos al final de este número.
Contador de uso
Estos 2 bytes se almacenan en una zona de la memoria de la Yubikey que no es volátil. Funciona como un contador que empieza en 0 y incrementa cada vez que conectamos la Yubikey a un puerto USB. 2 bytes soportan 65.536 combinaciones. Teniendo esto en cuenta, podríamos conectar nuestra Yubikey al puerto USB, 3 veces al día durante casi 60 años antes de que quede inutilizada por completo.
Timestamp
Al principio os dije que Yubikey se asemeja más a HOTP que TOTP, ya que no usa reloj para hacer sus cálculos. Pero estos 3 bytes siguientes se utilizan a modo de "falso reloj". Su valor se almacena en una zona de la memoria volátil, así que cada vez que desenchufamos la Yubikey, este reloj vuelve a valer 0. Mientras está conectado al puerto USB, estos 3 bytes se van incrementando como si fuese un cronómetro en marcha. Funciona con un reloj interno de 8Hz, así que queda agotado a los 24 días. Esto significa que no podremos dejar enchufada nuestra Yubikey por más de 24 días seguidos, ya que el contador dejaría de incrementarse y no se podrían generar más OTPs válidos, hasta re-conectar la Yubikey al USB.
Contador de uso de sesión
Éste único byte es un contador, que empieza en valor 0 y se incrementa en 1 cada vez que generamos un nuevo OTP. De esta manera, con nuestra Yubikey enchufada al USB, podemos generar diferentes códigos de forma continuada. Almacena un total de 255 combinaciones, tras ese valor vuelve a 0 y continua de nuevo.
Números random
Los siguientes 2 bytes son números aleatorios, para añadir entropia y dificultad al algoritmo.
Checksum CRC16
Finalmente, los 2 últimos bytes son una comprobación de integridad de todos los valores generados anteriormente. No voy a entrar en detalle con esto, pero pensad en la letra del DNI. Este checksum funciona similar y evita/detecta si un código se ha modificado por error
Final
Una vez tenemos nuestro código de 16 bytes generado, éste se encripta usando el algoritmo AES-128 y un password único que está grabado internamente en nuestra Yubikey. Cada yubikey tiene su própio password AES128 único. Pongamos un ejemplo inventado:
- ID Privado: `112233445566` (éste es mi ID privado)
- Contador uso: `0003` (he conectado un total de 3 veces mi Yubikey al USB)
- Timestamp: `000060` (llevo 60 segundos con la Yubikey enchufada al USB)
- Contador sesión: `00` (todavía no he generado ningún OTP, así que el contador es 0)
- Random: `1234` (números aleatorios)
- Checksum CRC16: `AAAA` (comprobación de integridad)
Mi código de 16 bytes es: 1122334455660003000060001234AAAA
, así que ahora la Yubikey lo encripta usando AES128 y un password secreto que no conocemos, el resultado podría ser: b86aa353632c721bce1cb4c46f609534
Ahora se añade, al inicio del código, nuestra ID pública en texto plano, por ejemplo 0000012dacbd
+ b86aa353632c721bce1cb4c46f609534
Y por último, aplicamos ModHex para traducir ese hexadecimal a letras: cccccbdtlrntnjhllegehedridbnrubrnfrfhvhckgef
Así de fácil nuestra Yubikey genera esa cadena de texto, y ahora sabemos que la parte inicial estática corresponde a nuestro ID público y el resto es el propio OTP generado. Vamos a fisgar un poco más. Veamos ahora la parte de la encriptación AES128
Encriptación AES-128 (pinchazo final)
La encriptación AES es de tipo simétrica. Esto significa que se utiliza un único password para encriptar y desencriptar. Ya hemos visto que el OTP generado por nuestra Yubikey es calculado en base a diferentes contadores (de sesión, de tiempo, de uso, ...) y luego encriptado usando AES, así que Twitter (o la aplicación de turno) tendría que conocer esa clave AES para hacer el camino contrario; desencriptar la cadena de bytes hasta llegar a los contadores y comprobar que sean correctos. Pero... la aplicación no conoce la clave AES...
Aquí es donde entra el principal "fallo de diseño" que personalmente no me gusta nada. Esta clave AES es el núcleo principal de toda la seguridad de la Yubikey. Cuando se programa nuestra Yubikey, se genera la clave AES y se graba en su interior, y luego una copia de esta clave se guarda en una base de datos propiedad de Yubico, en sus servidores.
Cuando una aplicación tiene que validar una Yubikey, se contacta con el servidor de Yubico, se entrega el ID público de la Yubikey cccccbdtlrnt
junto con el resto del OTP y entonces es el servidor de Yubico quien va a buscar en su base de datos la clave AES asociada a la Yubikey cccccbdtlrnt
y desencripta el OTP para validarlo. Una vez se valida, el servidor devuelve un VALID
o INVALID
y la aplicación (Twitter en este ejemplo) nos permite, o no, el login.
Así de cutre.
Qué ocurre si ahora los servidores de Yubico desaparecen? O peor aún... que ocurre si descubren una brecha de seguridad y alguien dumpea la base de datos con todos los IDs y AES-keys de los usuarios? Esta es la parte que no me gusta de la Yubikey: Dependes completamente de un tercero (Yubico) y de sus servidores. Y toda la seguridad y custodia de esa AES-key depende de ellos.
La validación del algoritmo
Llegados a este punto, no es necesario seguir explicando más el proceso de validación, pero como ya estamos metidos en el ajo, vamos a terminar la faena.
Tenemos el OTP, Yubico conoce y usa la clave AES para desencriptar el código y tener acceso así al valor de cada contador. Lo primero que hará es coger la última parte (checksum) para comprobar que los datos sean íntegros y con el formato correcto. Luego se comprueba que el ID privado corresponda con su pareja de ID público que tiene apuntada en su base de datos.
Luego, utilizando el contador de sesión y el contador de usos se comprueba que éste OTP se haya generado después del último OTP válido. Aquí no hay reloj como en TOTP. No puedes saber cuando 2 OTP se han generado, pero si puedes saber cual se ha generado antes o después. Si generamos 5 OTP válidos, pero mandamos a validar el último de ellos, los otros 4 restantes quedarán invalidados. Es una medida de protección por si alguien te captura un OTP de Yubikey que no hayas usado. En el momento que uses la Yubikey de nuevo, los anteriores códigos serán inválidos.
Si el OTP pasa todas estas comprobaciones, se da por hecho que todo es correcto y se envía el OK a la aplicación autenticadora.
Valoración y opinión personal
No me gusta. Es cierto que Yubico ofrece la posibilidad de enviarte tu clave AES128 y el código fuente para montarte tú mismo un servidor de autenticación, así como un SDK para volver a programar la Yubikey y hacer que valide contra tu servidor personal y tu própia clave AES... pero esto es algo que nadie va a hacer. Por supuesto no un usuario estandar. Así que quedas ligado perpetuamente a los servidores de Yubico, su availabilidad y sus medidas de seguridad para que no les roben las AES-keys.
Estos handicaps a mí personalmente me ponen nervioso y por supuesto jamás utilizaría una Yubikey como único sistema de acceso para nada que sea mínimamente importante.
En cuanto a la seguridad no tengo nada que objetar. Entiendo que la AES-key integrada dentro de la Yubikey no se puede extraer fácilmente. Todo el algoritmo está bien diseñado en cuanto a seguridad y su único punto débil es que alguien pueda, de alguna manera, capturar y conocer tu clave AES. Cosa muy improbable a no ser que Yubico sufra una brecha.
El resto de su sistema me gusta mucho. La calidad es buena, el producto es bonito y fácil de usar. Además ofrecen una aplicación para vitaminar todavía más tu Yubikey:
Con ella podemos habilitar o deshabilitar funciones de nuetra llave, así como asignar una nueva función a la pulsación larga del botón. Haciendo que un simple toque genere los OTP de Yubico y una pulsación larga haga otra operación:
Por ejemplo que escriba un password estático (master) o una validación HOTP estandar. Tambien ofrecen una app para smartphones que permite tener un gestor de códigos TOTP y HOTP protegido por nuestro llavero. Esto significa que tendremos un generador de códigos protegido y encriptado, que solo se podrá utilizar con la previa posesión de la Yubikey. Esta app podría sustituir tu Authy o Google Authenticator, por ejemplo: https://www.yubico.com/products/yubico-authenticator/
Referencias