Anatomía de un Código QR de 2FA: la URI otpauth:// Explicada
Security Tools

Anatomía de un Código QR de 2FA: la URI otpauth:// Explicada

Cada vez que activas la verificación en dos pasos y una web te muestra un pequeño cuadro en blanco y negro para apuntar con el móvil, estás mirando uno de los objetos más discretamente estandarizados de la web moderna. El cuadro es un código QR, y lleva dentro una única línea de texto llamada Key URI — una cadena otpauth:// que le dice a tu app de autenticación todo lo que necesita para empezar a producir códigos de seis dígitos. Este artículo abre ese cuadro y nombra cada pieza de su interior, porque en cuanto sabes leer la URI a mano, la 2FA deja de ser magia y se convierte en un formato que controlas por completo. Si quieres la teoría profunda de cómo se calculan los propios códigos, nuestro artículo pilar complementario sobre cómo funciona la autenticación TOTP cubre la matemática del HMAC y el paso de tiempo; aquí nos centramos en el payload del alta.

El Código QR No Hace Nada Criptográfico

Lo más importante de entender — y el dato que sorprende a casi todo el mundo — es que el código QR no realiza ninguna criptografía en absoluto. No cifra, ni firma, ni hashea, ni protege nada. Un código QR es puramente una forma bidimensional de codificar texto para que una cámara pueda leer caracteres sin que un humano teclee. El estándar QR (ISO/IEC 18004) define nada más que cómo pintar bits como módulos claros y oscuros con corrección de errores para que el patrón sobreviva a una mancha o a un mal ángulo de cámara. Decodifica el cuadro y recuperas exactamente una cosa: una cadena de texto plano que empieza por otpauth://.

Eso significa que el código QR y el texto que contiene son informativamente idénticos. Cualquiera que pueda leer la URI puede construir el QR, y cualquiera que pueda decodificar el QR recupera la URI literalmente. No hay ningún secreto extra escondido en los píxeles. La comodidad del cuadro consiste enteramente en evitar errores de transcripción cuando un secreto Base32 largo tiene que pasar de una web a un teléfono — nada más. Ten presente esta equivalencia durante el resto de la guía, porque es la clave tanto para usar como para proteger la configuración de 2FA.

El Formato Key URI de un Vistazo

La cadena codificada en un QR de 2FA sigue el Key URI Format, una convención publicada originalmente por Google para Google Authenticator y adoptada desde entonces por prácticamente todas las apps de autenticación — Authy, Microsoft Authenticator, 1Password, Bitwarden, Aegis y las demás. Es una URI estándar, así que se descompone en las mismas piezas que cualquier URI: un esquema, un host, una ruta y una cadena de consulta. Puesta de forma abstracta se ve así:

  • otpauth://TIPO/ETIQUETA?PARAMETROS

Un ejemplo concreto y genuino — un secreto inventado para la cuenta ficticia [email protected] en un servicio llamado ACME Co — se lee:

  • otpauth://totp/ACME%20Co:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30

Cada campo de esa línea tiene un cometido definido. En las siguientes secciones los tomamos uno a uno, desde el esquema más a la izquierda hasta el último parámetro de consulta.

El Esquema: otpauth://

La URI abre con el esquema otpauth seguido de ://. Un esquema es la parte de cualquier URI que dice qué tipo de recurso viene a continuación y qué gestor debe procesarlo — https para páginas web, mailto para direcciones de correo y aquí otpauth para el alta de contraseñas de un solo uso. En un teléfono, registrar una app como gestora del esquema otpauth es lo que permite tocar un enlace otpauth:// y que tu autenticador se abra listo para añadir la cuenta. El esquema no es decoración; es la etiqueta de enrutamiento que convierte una cadena en una importación accionable de 2FA.

El Host: totp Frente a hotp

Justo después de otpauth:// viene el tipo, que ocupa la posición de host de la URI. Hay exactamente dos valores válidos, y nombran los dos algoritmos definidos por el IETF para contraseñas de un solo uso:

  • totp — Time-based One-Time Password, definido en RFC 6238. El código cambia según un horario de reloj fijo, típicamente cada 30 segundos. Esto es lo que casi cualquier web entiende hoy por "2FA".
  • hotp — HMAC-based One-Time Password, definido en RFC 4226. El código avanza mediante un contador entero que da un paso adelante cada vez que se usa un código, en lugar de por el reloj. Es mucho más raro en configuraciones de consumo y aparece sobre todo con tokens hardware.

La diferencia importa porque los dos tipos toman parámetros obligatorios distintos. Una URI totp puede especificar un period (la longitud de cada paso de tiempo); una URI hotp debe especificar en su lugar un counter (el valor inicial del contador móvil). Elegir el tipo equivocado en la importación produce códigos que el servidor rechazará siempre, porque la app y el servidor avanzarían con horarios incompatibles. Para una comparación en lenguaje llano de TOTP frente a los otros segundos factores que te pueden ofrecer, nuestro artículo pilar hermano sobre TOTP frente a SMS y passkeys pone cada uno en contexto.

La Etiqueta: emisor:cuenta, Codificada en URL

Tras el tipo viene una barra y luego la etiqueta, que es el nombre legible bajo el que aparecerá la cuenta en la lista de tu autenticador. La etiqueta es el segmento de ruta de la URI y sigue una convención concreta: un prefijo de emisor opcional, dos puntos literales y luego el nombre de la cuenta — Emisor:cuenta. En el ejemplo de arriba ese segmento es ACME%20Co:[email protected], que tu app muestra como "ACME Co" de proveedor y "[email protected]" de cuenta.

Como la etiqueta vive dentro de una URI, debe estar codificada en URL. Por eso el espacio de "ACME Co" aparece como %20: un espacio en crudo no es legal dentro de una ruta de URI, así que se codifica en porcentaje. Lo mismo se aplica a cualquier otro carácter reservado o no ASCII que pueda contener un nombre de emisor o de cuenta. Los dos puntos que separan el prefijo de emisor del nombre de cuenta son estructura significativa, así que unos dos puntos que fueran genuinamente parte de un nombre se codificarían a su vez como %3A para evitar confundirse con el separador. Cuando decodifiques un QR a mano, invierte la codificación en porcentaje para recuperar el nombre legible; cuando escribas uno, codifica los espacios y caracteres especiales o tu app podría partir la etiqueta por el sitio equivocado.

Anatomía de un Código QR de 2FA: la URI otpauth:// Explicada

Los Parámetros de Consulta

Todo lo que va tras el ? es un conjunto de pares clave=valor unidos por &, exactamente como la cadena de consulta de una URL web normal. Estos parámetros llevan la configuración criptográfica real. Los tomamos en orden de importancia.

secret — el único campo verdaderamente obligatorio

El secret es la clave compartida que tanto el servidor como tu app mantienen, y es el único campo sin el cual nada funciona. Va codificado en Base32 tal como define el RFC 4648 — un alfabeto de las veintiséis letras mayúsculas AZ y los seis dígitos 27, elegido específicamente porque evita caracteres visualmente confundibles como 0/O y 1/I y es seguro de teclear a mano. Se usa Base32 en lugar del más compacto Base64 precisamente porque estos secretos a veces tienen que leerse de una pantalla y escribirse manualmente, y el alfabeto restringido de Base32 sobrevive mucho mejor a eso. La app decodifica el texto Base32 de vuelta a los bytes crudos de la clave, y esos bytes alimentan el HMAC que genera cada código. Como el secreto es todo el juego, también es todo el riesgo — un punto al que volvemos más abajo.

issuer — a quién pertenece la cuenta

El parámetro issuer nombra el servicio dueño de la cuenta, y debería coincidir con el prefijo de emisor de la etiqueta. Existe en parte por redundancia y en parte porque es el más fiable de los dos sitios de donde las apps leen el nombre del proveedor. Especificarlo en ambos lugares — el prefijo de la etiqueta y el parámetro de consulta — es la práctica recomendada de tirantes y cinturón, y los desajustes entre ambos pueden hacer que una app muestre una entrada confusa o duplicada. No tiene peso criptográfico; es puramente para una visualización correcta y para que tu app agrupe y deduplique cuentas con sensatez.

algorithm — SHA1, SHA256 o SHA512

El parámetro algorithm selecciona la función hash usada dentro del HMAC que produce el código. Se reconocen tres valores: SHA1, SHA256 y SHA512. Si se omite el parámetro, el valor por defecto es SHA1, y — quizá contraintuitivamente — la inmensa mayoría de despliegues de 2FA del mundo real usan exactamente ese valor por defecto. La razón es interoperabilidad, no debilidad: dentro de la construcción de truncamiento del RFC 6238, la seguridad práctica de TOTP no depende de la resistencia a colisiones del hash subyacente, y SHA1 es el valor que toda app de autenticación tiene garantizado soportar. Si un servidor sí especifica SHA256 o SHA512, la app debe usar la función correspondiente o todos los códigos saldrán mal; un desajuste silencioso aquí es una causa clásica de tickets de "mis códigos nunca funcionan".

digits — 6, 7 u 8

El parámetro digits fija cuántos dígitos decimales tiene el código mostrado. El valor por defecto y casi universal es 6; algunos despliegues de mayor seguridad usan 8. Mecánicamente, el algoritmo produce un número grande y luego lo toma módulo diez elevado al número de dígitos, así que pedir ocho dígitos simplemente conserva más de ese número. Más dígitos suponen un espacio ligeramente mayor que un atacante debe adivinar dentro de una sola ventana de tiempo, aunque la protección dominante sigue siendo el corto periodo de validez más que la cantidad de dígitos. Como con el algoritmo, la app debe renderizar el mismo número de dígitos que el servidor espera o la comparación falla.

period — la longitud de un paso de tiempo

El parámetro period aplica solo a las URI totp y da el número de segundos que cada código permanece válido. El valor por defecto es 30, que es lo que anima el anillo de cuenta atrás de tu autenticador. Algunos despliegues usan 60 para una experiencia más suave en redes lentas. El periodo es lo que convierte el tiempo de reloj de pared en el contador discreto de pasos que el RFC 6238 alimenta al HMAC: el paso de tiempo actual es el tiempo Unix dividido por el periodo, redondeado hacia abajo a un entero. Un periodo equivocado hace que la app y el servidor calculen números de paso distintos y nunca coincidan — y un dispositivo cuyo reloj se ha desviado más de un paso o dos sufre el mismo fallo incluso con el periodo correcto, por lo que "comprueba que la hora de tu móvil esté en automático" es lo primero que dicen las guías de solución de problemas de 2FA.

counter — obligatorio solo para hotp

Por completitud: cuando el tipo es hotp en lugar de totp, la URI debe llevar un parámetro counter con el valor inicial del contador en vez de un period. Como hotp avanza por uso y no por tiempo, ambos lados deben coincidir en dónde arranca el contador. Rara vez te lo encontrarás en un alta con app de móvil, pero forma parte del mismo formato y conviene reconocerlo.

Leyendo la URI Completa de Vuelta

Junta las piezas y el ejemplo anterior se decodifica limpiamente. otpauth://totp/ACME%20Co:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 dice: esto es un token basado en tiempo (totp); pertenece a la cuenta [email protected] en ACME Co; la clave compartida es la cadena Base32 JBSWY3DPEHPK3PXP; los códigos se calculan con HMAC-SHA1, tienen seis dígitos y se renuevan cada treinta segundos. Ese es el contrato completo entre un servidor y una app de autenticación. No se intercambia nada más — ni contraseña de cuenta, ni datos personales, solo los parámetros necesarios para mantener dos relojes produciendo los mismos seis dígitos.

Qué Hacer con un QR que No Puedes Escanear

A veces la vía de la cámara simplemente no está disponible. Puede que estés configurando la 2FA en el mismo dispositivo que muestra el código, usando un autenticador de escritorio sin cámara, trabajando por una sesión remota, o lidiando con un respaldo impreso cuyo cuadro se escaneó mal. Como el QR es solo una codificación de texto, siempre hay una vía de texto, y vale la pena conocer ambas direcciones:

  • Usa la opción de "entrada manual" o "clave de configuración". Casi toda web que muestra un QR también expone el secreto subyacente como una corta cadena Base32 debajo, a menudo tras un enlace de "¿no puedes escanear?" o "introducir código manualmente". Teclea ese secret en la pantalla de alta manual de tu app, junto con el emisor y el nombre de cuenta, y — si el sitio usó ajustes no estándar — el algoritmo, la cantidad de dígitos y el periodo. Esto reconstruye la misma cuenta que habría dado el QR.
  • Recupera la URI otpauth:// completa directamente. Si tienes la URI cruda (algunos sitios te dejan revelarla o copiarla, y los gestores de contraseñas la guardan), puedes pegarla tal cual en apps que aceptan una URI, o pasar la cadena entera a una herramienta TOTP para empezar a generar códigos al instante. Contiene todos los parámetros, así que no queda nada que adivinar.
  • Decodifica la imagen del QR para obtener la URI. Si lo único que tienes es la imagen y ninguna clave manual, decodificar el QR con cualquier lector offline te devuelve la cadena otpauth:// exacta, de la que lees el secreto y los ajustes. Prefiere una herramienta que se ejecute localmente en lugar de una que suba la imagen, por las razones de la siguiente sección.

Nuestro generador TOTP está hecho justo para esto: pega una URI otpauth:// o teclea un secreto Base32 con tu algoritmo, dígitos y periodo elegidos, y produce el código en vivo de seis dígitos y la cuenta atrás enteramente en tu navegador, para que puedas verificar una configuración, migrar una cuenta o mantener un respaldo sin instalar nada.

Seguridad: Un QR de 2FA Es Tan Sensible como una Contraseña

Todo lo anterior lleva a una conclusión rotunda. Como el código QR no es más que un contenedor del secret, y el secreto es la base entera sobre la que se genera cada código futuro, un código QR de 2FA es exactamente tan sensible como una contraseña — posiblemente más, ya que es el segundo factor pensado específicamente para sobrevivir a una contraseña robada. Cualquiera que fotografíe tu QR de alta, le haga una captura, o lea la clave Base32 puede generar códigos válidos para tu cuenta indefinidamente, desde cualquier sitio, sin tocar jamás tu teléfono. Todo el sentido del segundo factor se evapora en el momento en que el secreto se filtra.

Las consecuencias prácticas se siguen directamente. No publiques códigos QR de alta en capturas, hilos de chat, tickets de soporte, documentos compartidos ni foros públicos; trata la imagen con el mismo cuidado que la propia contraseña. Desconfía de las páginas web de "escanea esto para comprobar tu QR" que te piden subir la imagen — esa subida entrega tu secreto al servidor de un desconocido. Prefiere herramientas que decodifican y generan localmente. Y si un secreto queda expuesto alguna vez, el arreglo es el mismo que para una contraseña filtrada: vuelve al servicio, desactiva y vuelve a dar de alta el autenticador para que se emita un secreto nuevo, lo que invalida permanentemente el antiguo. La comodidad del cuadrito es real, pero es comodidad envuelta alrededor de tu credencial más sensible, y merece manejarse así.

Los Estándares Detrás del Cuadro

Nada de esto es propietario. El Key URI Format otpauth:// es la convención ampliamente documentada que surgió de Google Authenticator y hoy sustenta la interoperabilidad entre apps. Los algoritmos de generación de códigos que configura son estándares abiertos del IETF: el RFC 4226 define HOTP, el esquema basado en HMAC que usa el modo contador; el RFC 6238 define TOTP, añadiendo el paso de tiempo sobre HOTP; y el RFC 4648 define la codificación Base32 que lleva el secreto de forma segura por texto y por QR por igual. El propio formato de imagen QR es ISO/IEC 18004. Como cada capa es pública, cualquier app puede implementar alta y generación de forma compatible — que es exactamente por qué un cuadro de una web funciona en el autenticador que prefieras.

La Conclusión

Un código QR de 2FA no es un candado; es una etiqueta. Codifica una única línea otpauth:// cuyo esquema la enruta a tu autenticador, cuyo host totp o hotp nombra el algoritmo, cuya etiqueta emisor:cuenta nombra la cuenta, y cuyos parámetros de consulta — secret, issuer, algorithm, digits y period — configuran exactamente cómo se producen los códigos. El cuadro en sí no hace criptografía alguna; solo te ahorra teclear un secreto Base32. Entiende eso y ganas dos poderes: siempre puedes recurrir al texto cuando la cámara falla, y sabes que debes guardar el secreto con el mismo cuidado que cualquier contraseña. Cuando quieras ver una URI convertirse en códigos en vivo, abre el generador TOTP y disecciona uno de los tuyos.

← Volver al Blog