Saltar al contenido
LINWIND de Windows a Linux

~/seguridad

Cómo funciona el código de 6 cifras de las apps de verificación

Marcos · · 9 min

Cuando escaneas el QR para activar la verificación en dos pasos, tu móvil y el servicio se quedan cada uno con una copia del mismo secreto. Desde ese momento, cada 30 segundos los dos hacen por separado la misma cuenta con ese secreto y con la hora, y se quedan con seis cifras del resultado. Si el secreto y la hora coinciden, el número coincide, y el móvil no necesita hablar con nadie para sacarlo. El método se llama TOTP y es público. Lo describe la RFC 6238 sobre el HOTP de la RFC 4226, y Microsoft incluye su Authenticator entre los generadores TOTP en la documentación de Entra.

Lo que guarda el móvil al escanear el QR

Ese cuadrado de puntos no es más que un texto. Según el formato que documenta Google para su app, dentro va una dirección que empieza por otpauth://totp/ con el nombre de la cuenta, el servicio y un parámetro secret, que es la clave escrita en Base32 (letras y números). La app guarda esa clave, el servicio guarda la suya, y ya no vuelven a intercambiarla.

La RFC 6238 pide que cada usuario tenga una clave distinta y que se genere al azar. Por eso dos personas en el mismo servicio ven números distintos en el mismo segundo.

La hora, partida en tramos de 30 segundos

La otra mitad de la cuenta es el tiempo. El móvil cuenta los segundos que han pasado desde la medianoche del 1 de enero de 1970 en hora universal (lo que se llama tiempo Unix), divide entre 30 y se queda con la parte entera. Ese número de tramo entra en el cálculo. En el ejemplo de la RFC, en el segundo 59 el tramo es 1 y en el segundo 60 ya es 2.

Mientras dure el tramo, el código es el mismo, y al cambiar de tramo sale otro. La norma recomienda esos 30 segundos como equilibrio entre seguridad y comodidad, porque un tramo más largo deja más tiempo a quien vea tu código para usarlo antes que tú. La RFC también obliga al servicio a rechazar el mismo código si ya lo aceptó una vez, aunque siga dentro de su tramo.

De la cuenta HMAC a seis cifras

Con el secreto y el número de tramo, la app calcula un HMAC, que es una especie de huella que solo puede producir quien tiene la clave. Con SHA-1, el algoritmo base del estándar, salen 20 bytes, demasiado para teclearlo, así que la sección 5.3 de la RFC 4226 describe cómo recortarlo, en tres pasos.

  1. La app mira los últimos 4 bits del último byte, que dan un número entre 0 y 15, y lo usa como posición de partida.
  2. Desde esa posición toma 4 bytes seguidos y apaga el primer bit, para que el número no salga negativo en ningún procesador.
  3. Divide ese número entre un millón y se queda con el resto, que siempre tiene seis cifras como mucho (si sale más corto, se rellena con ceros por delante).

La norma trae un ejemplo resuelto en el que los 4 bytes elegidos valen 0x50ef7f19 y el código final es 872921. La norma exige un mínimo de seis cifras y deja abierto usar siete u ocho.

Por qué funciona sin internet

Ni el secreto ni la hora vienen de fuera en el momento de generar el código. El secreto está guardado en el móvil desde que escaneaste el QR, y la hora la lleva el propio teléfono. Google lo dice en la ayuda de Google Authenticator: puedes generar códigos «aunque no tengas conexión a Internet ni cobertura móvil».

Pon el móvil en modo avión, abre la app y mira cómo el código sigue apareciendo y cambia cada 30 segundos. Si con el móvil todavía en modo avión entras en ese servicio desde el ordenador y tecleas el número, el servicio lo acepta, porque ha hecho la misma cuenta en su lado.

Compruébalo con los vectores de prueba de la RFC

El apéndice B de la RFC 6238 publica una tabla para que cualquier programa compruebe que calcula bien. Usa el secreto de prueba 12345678901234567890, tramos de 30 segundos y códigos de ocho cifras. El de seis cifras son las seis últimas, porque sale del mismo número dividido entre un millón en vez de entre cien millones. Estas son las filas con SHA-1:

Segundos desde 1970 Fecha y hora UTC Tramo Código en la RFC (8 cifras) Código de 6 cifras
59 1-1-1970 00:00:59 1 94287082 287082
1111111109 18-3-2005 01:58:29 37037036 07081804 081804
1111111111 18-3-2005 01:58:31 37037037 14050471 050471
1234567890 13-2-2009 23:31:30 41152263 89005924 005924
2000000000 18-5-2033 03:33:20 66666666 69279037 279037
20000000000 11-10-2603 11:33:20 666666666 65353130 353130

Fíjate en la segunda y la tercera fila. Entre una y otra hay solo dos segundos, pero en medio cae un cambio de tramo y el código ya es otro.

La primera fila cuadra con el apéndice D de la RFC 4226, donde el mismo secreto con el contador a 1 da 287082. Es el mismo cálculo, porque el TOTP es un HOTP cuyo contador es el tramo de tiempo.

Si tienes Python a mano, estas líneas repiten la primera fila con el secreto de prueba de la RFC (nunca con uno tuyo):

import hmac, hashlib, struct
secreto = b"12345678901234567890"   # secreto de prueba de la RFC
tramo = 59 // 30                    # segundo 59, tramo 1
h = hmac.new(secreto, struct.pack(">Q", tramo), hashlib.sha1).digest()
pos = h[-1] & 0x0F
n = int.from_bytes(h[pos:pos + 4], "big") & 0x7FFFFFFF
print(str(n % 10**6).zfill(6))      # 287082

Cambia el 59 por 1111111109 o 1111111111 y saldrán los códigos de la tabla. Los números de esta sección se recalcularon así y coinciden con las dos RFC.

Por qué la hora del móvil tiene que estar bien

Si el reloj del móvil va adelantado o atrasado, el teléfono calcula un tramo distinto del que calcula el servidor, y el número no coincide. La RFC recomienda que el servicio acepte como mucho un tramo de retraso por lo que tarda el código en llegar, y en su sección 6 pone el ejemplo de un servidor que admite dos tramos hacia atrás, unos 89 segundos de desfase. Cada servicio decide su margen. Microsoft, por ejemplo, documenta un minuto arriba o abajo para los tokens OATH físicos de 30 segundos en Entra.

Google Authenticator tenía un ajuste propio para corregir la hora, pero su ayuda explica que desde la versión 7.0 ya no existe y que la app usa la hora del sistema operativo. Entre las causas de un código rechazado cita que la hora no esté sincronizada o no sea correcta para tu zona horaria. Lo de la zona tiene su porqué. El código va con la hora universal, así que un móvil con la zona mal puesta y la hora corregida a mano para que «se vea bien» lleva por dentro una cuenta desplazada en horas. Lo más seguro es dejar la fecha, la hora y la zona en automático.

Código por SMS, app y llaves de acceso

El código por SMS lo genera el servicio y te llega por la red del operador, así que depende de tu número. INCIBE explica en su artículo sobre el SIM swapping cómo los delincuentes sacan un duplicado de tu SIM haciéndose pasar por ti y reciben los SMS de verificación, y entre sus consejos está usar Google Authenticator o Microsoft Authenticator. Con la app, el secreto no viaja cada vez, y un duplicado de tu SIM no lo trae consigo.

Las llaves de acceso (passkeys) van por otro camino. Según Apple, el dispositivo crea un par de claves, el servicio solo guarda la pública y «no se transmite ningún secreto compartido». Y Google añade que no se pueden compartir, copiar ni dar por error a otra persona, que es justo el punto débil de cualquier código. Microsoft, en su comparativa de métodos, dice que el SMS, los códigos de un solo uso y las apps de autenticación son propensos a la suplantación remota, y recomienda las llaves FIDO2. Yo, en las cuentas que ya las admiten, pondría una llave de acceso y dejaría la app como método de reserva.

El riesgo real es la web falsa que te pide el código

Nadie necesita adivinar tu código si consigue que se lo escribas tú. INCIBE recoge un caso en su artículo «¡Me han secuestrado mi cuenta!». Un correo falso llevaba a una página que pedía la contraseña y, minutos después, el código de verificación recibido por SMS. Con los dos datos, los delincuentes entraron «saltándose hasta el doble factor de autenticación» y cambiaron los datos de recuperación. El de la app vale lo mismo para el ladrón mientras no caduque su tramo, y la RFC 6238 ya advierte que si un tercero ve el código antes de que lo uses, puede usarlo él dentro de ese margen.

INCIBE aconseja no facilitar a nadie los códigos que recibas, y en su artículo sobre el secuestro de WhatsApp, lo resume en «Nadie debería pedírnoslo, pero si lo hacen, debemos desconfiar». El único sitio donde tiene sentido escribir el código es la pantalla de inicio de sesión a la que llegaste tú. Antes de teclearlo, mira la dirección de la página. Si dudas, aquí tienes cómo comprobar si una web es segura.

Consejos para no quedarte fuera de tus cuentas

Guarda los códigos de recuperación. INCIBE recomienda guardarlos en un lugar seguro, y Google explica en su ayuda de códigos alternativos que te da 10, que cada uno sirve una sola vez y que puedes imprimirlos y guardarlos junto al pasaporte u otros documentos importantes. Son lo que te salva si pierdes el móvil.

No hagas captura del QR al activarlo. Lleva dentro el secreto, y como el código solo depende del secreto y de la hora, quien tenga esa imagen puede generar los mismos números que tú en cualquier otro aparato. Lo mismo vale para los QR que crea Google Authenticator al exportar códigos. Si sospechas que alguien vio el QR, quita la app de ese servicio y vuelve a configurarla con un QR nuevo.

Prepara el cambio de móvil antes de borrar el viejo. Si inicias sesión con tu cuenta de Google en Google Authenticator, los códigos se sincronizan y en el móvil nuevo aparecen solos. Si la usas sin cuenta, su ayuda explica cómo pasarlos con la opción de transferir códigos, exportando en el viejo e importando en el nuevo con un QR. Y si pierdes el teléfono o te lo roban, Google aconseja borrarlo a distancia cuanto antes.

El código protege lo que hay detrás de una contraseña, y la contraseña sigue contando. Si quieres revisar esa parte, aquí tienes cómo crear una contraseña segura y no tener que recordarlas todas.

# sigue leyendo

Comentarios

Deja un comentario

Tu correo no se publica.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.