PureTools

TOTP: Cómo Funciona Realmente la Autenticación de Dos Factores

PureTools Team· 10 min de lectura
TOTP: Cómo Funciona Realmente la Autenticación de Dos Factores

TOTP: Esos Códigos de 6 Dígitos No Son Magia

Cada 30 segundos, tu app autenticador genera un nuevo código de 6 dígitos. Sin conexión a internet. Sin comunicación con servidor. ¿Cómo sabe el servidor qué código estás viendo? Porque ambos lados están ejecutando el mismo algoritmo con el mismo secreto.

El Algoritmo

TOTP (Time-Based One-Time Password, RFC 6238) funciona en 4 pasos:

  1. Obtener el tiempo actual como timestamp Unix
  2. Dividir por el time step (30 segundos por defecto) → esto da un valor de contador
  3. HMAC-SHA1 del contador con el secreto compartido → produce un hash de 20 bytes
  4. Truncar el hash a un número de 6 dígitos
// Pseudocódigo
time_step = floor(unix_timestamp / 30)
hmac = HMAC-SHA1(secret, time_step)
offset = hmac[19] & 0x0f
code = (hmac[offset..offset+3] & 0x7fffffff) % 1000000
// Resultado: código de 6 dígitos como "482901"

Por Qué Funciona Sin Internet

Durante el setup, el servidor genera un secreto aleatorio y lo comparte con tu autenticador (vía QR code). Ambos lados ahora tienen:

  • El mismo secreto (típicamente 20 bytes, codificado en Base32)
  • El mismo algoritmo (HMAC-SHA1)
  • El mismo time step (30 segundos)
  • Relojes sincronizados (dentro de tolerancia razonable)

El servidor no necesita contactar tu celular. Solo ejecuta el mismo cálculo con el mismo secreto y verifica si los códigos coinciden.

El QR Code

Cuando escaneas un QR code TOTP, contiene una URI como:

otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp&algorithm=SHA1&digits=6&period=30
ParámetroValorSignificado
secretJBSWY3DPEHPK3PXPSecreto compartido codificado en Base32
issuerMyAppNombre del servicio mostrado en el autenticador
algorithmSHA1Algoritmo HMAC (SHA1/SHA256/SHA512)
digits6Longitud del código (6 u 8)
period30Time step en segundos

Implementación en JavaScript

// Verificación TOTP en Node.js
import { createHmac } from 'crypto';

function generateTOTP(secret: Buffer, timeStep = 30): string {
  const counter = Math.floor(Date.now() / 1000 / timeStep);

  // Convertir contador a buffer big-endian de 8 bytes
  const counterBuffer = Buffer.alloc(8);
  counterBuffer.writeBigInt64BE(BigInt(counter));

  // HMAC-SHA1
  const hmac = createHmac('sha1', secret).update(counterBuffer).digest();

  // Truncamiento dinámico
  const offset = hmac[hmac.length - 1] & 0x0f;
  const code = (
    ((hmac[offset] & 0x7f) << 24) |
    ((hmac[offset + 1] & 0xff) << 16) |
    ((hmac[offset + 2] & 0xff) << 8) |
    (hmac[offset + 3] & 0xff)
  ) % 1000000;

  return code.toString().padStart(6, '0');
}

// Verificar con ventana ±1 para drift de reloj
function verifyTOTP(secret: Buffer, token: string): boolean {
  for (let i = -1; i <= 1; i++) {
    const adjustedTime = Date.now() + i * 30000;
    // ... generar código en tiempo ajustado
    if (generatedCode === token) return true;
  }
  return false;
}

Implementación Python

import hmac
import hashlib
import struct
import time
import base64

def generate_totp(secret_b32: str, time_step: int = 30) -> str:
    secret = base64.b32decode(secret_b32)
    counter = int(time.time()) // time_step
    counter_bytes = struct.pack('>Q', counter)

    hmac_hash = hmac.new(secret, counter_bytes, hashlib.sha1).digest()

    offset = hmac_hash[-1] & 0x0f
    code = (
        struct.unpack('>I', hmac_hash[offset:offset + 4])[0] & 0x7fffffff
    ) % 1000000

    return f"{code:06d}"

# Ejemplo
print(generate_totp('JBSWY3DPEHPK3PXP'))
# "482901" (cambia cada 30 segundos)

Consideraciones de Seguridad

  • Almacena secretos cifrados. El secreto TOTP en tu base de datos es equivalente a una contraseña. Cífralo en reposo.
  • Proporciona códigos de respaldo. Los usuarios pierden celulares. Genera 8-10 códigos de uso único durante el setup.
  • Permite drift de reloj. Acepta códigos del time step anterior y siguiente (±30 segundos).
  • Rate limit en la verificación. Sin rate limiting, un atacante tiene 1/1.000.000 de probabilidad por intento — brute-forceable a alta velocidad.
  • No uses SMS. TOTP (app autenticador) es más seguro que 2FA basado en SMS, que es vulnerable a SIM swapping.

TOTP vs HOTP

FeatureTOTP (Basado en Tiempo)HOTP (Basado en Contador)
ContadorTiempo actual / 30Entero incrementado
ExpiraciónCódigos expiran después de 30sCódigos nunca expiran
Problemas de syncDrift de relojDesync de contador
SeguridadMejor (códigos expiran)Menos seguro (replay posible)
Usado porGoogle Authenticator, AuthyYubiKey (modo HOTP), algunos tokens físicos

Genera códigos TOTP: Generador TOTP — ingresa una clave secreta y ve el código en vivo con temporizador de cuenta regresiva.