Realmente me gusta el nivel de seguridad proporcionado por U2F, pero junto con la seguridad, debe considerar un plan de recuperación. Perder el acceso a sus cuentas más importantes, si algo sucede con el token U2F principal, es un problema grave. Al mismo tiempo, me gustaría evitar usar una copia de seguridad que comprometa la seguridad proporcionada por U2F.
Métodos de respaldo populares
Hasta la fecha, ha sido una buena práctica tener un segundo token U2F independiente para respaldo; este token debe agregarse manualmente a cada servicio y almacenarse en un lugar "seguro". Otra práctica común es utilizar el método no U2F como respaldo (OTP, códigos de recuperación). Honestamente, ambos métodos dejan mucho que desear.
Token U2F independiente
Esto significa que cada vez que me registro en algún servicio nuevo, necesito agregar mis dos tokens. Este hecho plantea una serie de problemas:
- Un token de respaldo debe ser bastante fácil de acceder. A pesar de que no lo llevaré conmigo en un llavero, debería poder alcanzarlo rápidamente, por lo que difícilmente puedo encontrar algo mejor que guardarlo en casa. Cuán real es seguro, incluso si se usa una caja fuerte, puede hablar durante mucho tiempo;
- Cuando tengo que registrarme para un servicio mientras estoy fuera de casa, no puedo agregar un token de respaldo. Por lo tanto, debe intentar recordar que necesita agregarlo más tarde, y hasta que esto suceda, no hay copia de seguridad. En el peor de los casos, puedo olvidarme completamente de él;
- Cuando estoy en casa, mis dos fichas están en el mismo lugar. Este método de copia de seguridad está lejos de ser ideal: ambos tokens pueden no estar disponibles debido a un incidente (ser destruido o robado);
- El hecho de que el token de respaldo se almacene en casa es completamente obvio. Si alguien realmente quiere llegar a mi ficha, ya sabe dónde buscarla;
- Método no universal: no todos los servicios le permiten agregar más de una clave a su cuenta.
En mi opinión, esta "práctica ejemplar" no es muy confiable y más bien gravosa. Veamos otra práctica común.
Método no U2F como respaldo
OTP:
- Usar OTP como respaldo es mejor que usarlo como el método 2FA principal, pero el hecho de tener OTP de alguna manera abre un vector de ataque adicional;
- Los teléfonos se descomponen, se pierden y son robados, y si después de su pérdida existe la posibilidad de que esté en manos de extraños, entonces debe recuperar manualmente esta copia de seguridad en todas las cuentas;
- Siempre llevo un teléfono y un token U2F conmigo, así que de nuevo, este método de respaldo está lejos de ser ideal: la probabilidad de perderlos de inmediato es mucho mayor que si el respaldo se almacenara por separado. Pero este elemento puede compensarse ligeramente utilizando, por ejemplo, Authy, que almacena la copia de seguridad cifrada en su servidor;
- Método no universal: desafortunadamente, hay una cantidad suficiente de servicios que ofrecen solo aplicaciones personalizadas y no son compatibles con TOTP estándar.
Códigos de recuperación:
- Los códigos de recuperación deben almacenarse en un lugar seguro. Una vez más, este "lugar seguro" probablemente será mi hogar, con casi los mismos problemas que un token U2F separado;
- Una vez más, un método no universal: cada servicio tiene su propio enfoque de copia de seguridad
Para resumir, todos estos métodos son no universales, pesados y no demasiado seguros.
El mejor método de respaldo
Ahora, después de haber criticado suficientemente el estado actual de las cosas, finalmente diré lo que realmente quiero. Realmente quiero tener dos tokens U2F: primario y de respaldo, pero deben configurarse de cierta manera:
- Cuando registro el token principal en cualquier dispositivo, el token de respaldo se vuelve operativo automáticamente para este servicio;
- Tan pronto como use un token de respaldo en cualquier servicio, el token principal no es válido para este servicio.
Antes de discutir la viabilidad técnica de esto dentro de U2F, explicaré por qué es genial y cómo lo uso.
Porque es genial
Si observamos las críticas al token de copia de seguridad independiente descrito anteriormente, podemos ver que se eliminan todas las deficiencias de este método:
- El token de respaldo ya no debería ser fácilmente accesible. Ejemplos extremos pueden ser: tapar una ficha dentro de una pared de ladrillos, o enterrar un metro y medio en un jardín o en otro lugar. No es broma, estoy bastante listo para hacerlo;
- Independientemente de dónde esté ubicado, si me registro en algún servicio, no necesito hacer nada para agregar un token de respaldo a este servicio. Solo uso mi token principal, y estoy tranquilo, sabiendo que tengo una copia de seguridad;
- Para los extraños, no está completamente claro dónde se encuentra mi token de respaldo. Incluso sabiendo que existe, tratar de encontrarlo usted mismo apenas tiene sentido;
- Es lo suficientemente seguro. Incluso si algo malo le sucede a mi token principal, es muy poco probable que el mismo incidente afecte al token de respaldo;
- Es universal. Este método de respaldo funcionará en cualquier servicio que admita U2F, independientemente de qué más admita este servicio.
Y si algo malo sucede realmente con el token principal, entonces hago lo siguiente:
- Excavo / no tengo claro un token de respaldo;
- Autenticarme en todos mis servicios con U2F, cancelando así el token principal;
- Ordeno un nuevo par de tokens y, una vez recibido, agrego un nuevo token principal en todos los servicios y revoco el antiguo.
Al menos para mí personalmente, esta estrategia es un gran compromiso para un alto nivel de seguridad y una carga fácil de respaldo. Es más seguro y más confiable que cualquier otro método.
Implementación
Descripción general del protocolo U2F
Antes de que podamos hablar sobre la implementación, debemos entender en cierto nivel cómo funciona U2F. La mayoría de los fabricantes lo implementan de la siguiente manera (no todos los siguientes están presentes en el estándar; algunas cosas son detalles de implementación, pero la mayoría de las implementaciones existentes, hasta donde yo sé, funcionan de esa manera):
device_secret
programa en el token U2F, junto con un
counter
32 bits, que solo se puede incrementar. Cuando registramos un token U2F en un servicio, sucede lo siguiente:
- El navegador envía el
AppID
(de hecho, el nombre de dominio) al dispositivo U2F; - El dispositivo genera un número aleatorio (
nonce
), lo combina con el AppID
, lo pasa todo a través de HMAC-SHA256 usando device_secret
como clave, y el hash resultante se convierte en la clave privada para este servicio en particular: service_private_key
; - Desde
service_private_key
, service_public_key
genera la clave pública service_public_key
; - El dispositivo toma el
AppID
nuevamente, lo combina con service_private_key
, y lo pasa nuevamente a través del HMAC-SHA256 usando device_secret
como clave. El resultado ( MAC
), junto con el nonce
que se generó anteriormente, se convierte en key_handle
; - El dispositivo
key_handle
y service_public_key
al navegador, y el navegador pasa al servicio, que guarda estos datos para futuras autenticaciones.
La autenticación posterior se realiza de la siguiente manera:
- El servicio genera un
challenge
(datos generados aleatoriamente) y lo envía al navegador junto con key_handle
(que consiste en nonce
y MAC
). El navegador pasa todo esto al dispositivo, junto con el AppID
(es decir, el nombre de dominio); - El dispositivo, que tiene
nonce
y AppID
, genera service_private_key
de la misma manera que se generó durante el registro; - El dispositivo genera un
MAC
de la misma manera que durante el registro, y al compararlo con el MAC
recibido del navegador, se asegura de que nonce
no sea reemplazado y, por lo tanto, service_private_key
confiable; - El dispositivo incrementa el
counter
; - El dispositivo firma el
challenge
, el AppID
y el counter
utilizando service_private_key
, y envía la firma resultante ( signature
) y el counter
navegador, que transfiere estos datos al servicio; - El servicio verifica la
signature
usando service_public_key
que tiene después del registro. Además, la mayoría de los servicios verifican que el counter
mayor que el valor anterior (si esta no es la primera autenticación). El propósito de esta prueba es hacer que la clonación de dispositivos U2F sea inaccesible. Como resultado, si la signature
coincide y el counter
mayor que el valor anterior, la autenticación se considera completada con éxito y el servicio guarda el nuevo valor del counter
.
Ahora describamos los detalles que están directamente relacionados con la discusión.
Detalles de interes
El primero es que el dispositivo no almacena
service_private_key
para cada servicio: en su lugar, muestra
service_private_key
cada vez que usa HMAC-SHA256. Esto es muy importante para nosotros: es obvio que si cada dispositivo almacenaría claves únicas por separado para cada servicio, solo este dispositivo podría autenticarse posteriormente.
Esto, por cierto, no es un requisito de U2F: U2F no indica cómo deben almacenarse las claves, y algunas implementaciones tempranas de U2F, de hecho, almacenaron claves para cada servicio por separado. Este enfoque tiene la desventaja de que el número de servicios para los cuales se puede usar el dispositivo es limitado. La derivación de service_private_key
elimina este inconveniente.Y en segundo lugar, el dispositivo tiene un
counter
para evitar la clonación.
A primera vista, puede parecer que este
counter
no nos permite implementar la estrategia de respaldo discutida (al menos eso me pareció cuando traté de encontrar una solución), pero de hecho, ¡solo nos ayuda! Te lo explicaré ahora.
Idea principal
La idea es esta: en la etapa de producción, programe dos tokens de manera que ambos tengan el mismo
device_secret
, pero el token de respaldo necesita alguna corrección: en lugar de usar el
counter
en su forma pura (como lo hacen los tokens ordinarios), debería agregar alguna gran constante para
counter
. Por ejemplo, la mitad del rango de 32 bits, es decir aproximadamente
2 000 000 000
, parece razonable: es poco probable que agote tantas autenticaciones en toda mi vida.
De hecho, eso es todo. Simple y efectivo.
Con dos tokens programados de esta manera, oculto el token de respaldo en un lugar
realmente difícil de alcanzar y nunca lo toco. Si sucede algo terrible y pierdo el acceso al token principal, aún accedo al token de respaldo, y puedo usarlo de inmediato en todos los servicios donde registré el token principal, porque La copia de seguridad tiene el mismo
device_secret
, y su
counter
comienza con un número realmente grande, que no obtendré por el resto de mi vida.
Además, llamo la atención sobre el hecho de que
no propongo hacer tokens clonados . Dos tokens, aunque tienen el mismo
device_secret
, tienen contadores diferentes, y después de programar
device_secret
no debería haber forma de recuperarlo del dispositivo o crear un clon de otra manera.
Una nota sobre el contador
Un lector atento puede notar que existe el siguiente problema de seguridad: ¿qué sucede si un atacante obtiene acceso al token principal e inicia de alguna manera 2,000,000,000 de autenticaciones? Luego obtiene acceso al servicio incluso después de que el token de copia de seguridad se haya utilizado en este servicio.
Afortunadamente, este problema tiene una solución simple. En cualquier caso, el contador debe implementarse en hardware (presumiblemente en algún procesador criptográfico), y para una implementación segura, este contador de hardware debe tener un rango de menos de 32 bits. Por ejemplo, en el
ATECC508A, los contadores solo pueden contar hasta 2097151, por lo que al establecer la constante agregada al contador en cualquier valor mayor que el valor máximo del contador, podemos estar seguros de que el token principal nunca puede contar para el contador en el token de respaldo.
Para aclarar: supongamos que nuestro token U2F usa ATECC508A y denota el contador dentro del ATECC508A como
hw_counter
. Entonces:
- En el token principal, usamos para los cálculos:
hw_counter
; - En el token de respaldo, usamos para los cálculos:
hw_counter + 2000000000
.
Tenga en cuenta que no modificamos el
hw_counter
real dentro del procesador de cifrado; seguirá contando de 0 a 2097151. En cambio, cada vez que necesitamos obtener el valor del contador, leemos
hw_counter
de ATECC508A, luego agregamos nuestra constante y la devolvemos (para cálculos adicionales para U2F).
Por lo tanto, el rango de valores de contador en el token principal será [0, 2097151], mientras que el rango de valores de contador en el token de respaldo será [2000000000, 2002097151]. El hecho de que estos rangos no se superpongan garantiza la cancelación del token principal cuando se usa la copia de seguridad (si el servicio usa
counter
; los servicios principales que verifiqué lo usan).
Implementación real
Ninguno de los fabricantes de tokens U2F que conozco admite la personalización requerida hoy. Pero afortunadamente, hay una implementación de código abierto del token U2F:
SoloKeys .
Escribí mi artículo original (en inglés) hace un año, y esta parte está un poco anticuada: entonces SoloKeys estaba en la etapa de creación de prototipos, y utilicé la iteración anterior del proyecto:
u2f-zero . Por lo tanto, no traduciré esta parte ahora, ya que la única forma de obtener un dispositivo u2f-zero es soldarlo usted mismo, y no es aconsejable hacerlo (aunque hay instrucciones en el github).
Sin embargo, todos los detalles de la modificación necesaria de u2f-zero se dan en el
artículo original .
Cuando mis manos lleguen a los solos, escribiré instrucciones para su modificación.
De una forma u otra, esta es la única forma que conozco hoy para obtener un token U2F que funcione con una copia de seguridad confiable. La comprobación de varios servicios (al menos google y github) mostró que funciona: al registrar el token principal en el servicio, también podemos usar la copia de seguridad, y después del primer uso de la copia de seguridad, el token principal deja de funcionar. Awwwwwww. <3
Advertencia
A pesar de que esta estrategia de respaldo es genial, no estoy tan seguro de su implementación específica a través de u2f-zero o solokey. Este camino es la única forma de obtener lo que quieres, así que fui por ese camino; pero suponiendo que el atacante obtenga acceso físico al dispositivo U2F, no estoy seguro de que piratear el dispositivo (es decir, obtener
device_secret
de él) sea tan difícil como lo sería en el caso de Yubikey u otros fabricantes importantes. Los autores de solokey afirman que "el nivel de seguridad es el mismo que en la llave de un automóvil moderno", pero no realicé ningún examen para confirmarlo.
Sin embargo, para ser honesto, no estoy realmente preocupado por esto. Si un atacante simplemente roba un token sin la intención de devolverlo, entonces la complejidad de romperlo no importa, porque un atacante puede simplemente usar este token para acceder a una cuenta y, por ejemplo, simplemente revocar este token y agregar otro. Sin embargo, para esto también debo tener otros problemas de seguridad graves. El token U2F es solo el segundo factor.
Por lo tanto, el único escenario en el que solokey puede ser menos seguro que otra cosa es cuando un atacante intenta acceder al dispositivo durante un corto período de tiempo, obtener
device_secret
de él y devolverme el dispositivo, invisible para mí. Para hacer esto, necesita leer el contenido del microcontrolador flash (o RAM en el momento adecuado), y esto no es muy trivial.
Teniendo en cuenta todos los factores, creo que para mí personalmente tener una copia de seguridad confiable es mucho más importante que tener una implementación de hardware ultra segura de un dispositivo U2F. La probabilidad de problemas con una implementación tan segura y la falta de una buena copia de seguridad es mayor que la probabilidad de problemas con u2f-zero (solokey) y la copia de seguridad.
Conclusión
La estrategia de respaldo considerada supera a las alternativas en todas las dimensiones: es universal, más segura y más confiable que cualquier otro método.
Me alegrará si al menos uno de los principales fabricantes implementa esto en sus productos, pero aún no hay certeza. Un tipo de soporte de Yubico, James A., incluso me dijo que la copia de seguridad es lo que necesito, "no es posible con la forma en que está diseñado U2F", y después de establecer los detalles de implementación, simplemente dejó de responder.
Afortunadamente, esto no fue tan imposible como cree Yubico.
Mi artículo original en inglés: Copia de seguridad confiable, segura y universal para el token U2F . Porque el autor del artículo original es yo mismo, entonces, con su permiso, no puse este artículo en la categoría de "traducción" .