Saltar al contenido
verificaOTP

Capítulo 2 de 5 · 15 min de lectura

Contadores de intento y lockout

Implementa límites que un atacante no pueda resetear pidiendo un código nuevo.

El contador ingenuo vive en la verificación: cinco intentos y se bloquea. Pero si el atacante puede pedir una verificación nueva, obtiene cinco intentos más, indefinidamente. El contador que importa vive en el sujeto: el número, la cuenta y la IP.

En el playground

El código implementa un contador por número sobre el SDK simulado. Ejecuta un ataque de fuerza bruta y observa cómo el lockout corta la secuencia aunque el atacante pida códigos nuevos.

Lockout sin denegación de servicio

Un lockout permanente por número permite que un atacante bloquee a la víctima a propósito. Usa bloqueos temporales crecientes (15 minutos, 1 hora, 24 horas) y ofrece una ruta de recuperación por otro canal. El objetivo es frenar la adivinanza, no castigar al dueño legítimo.

Backoff progresivo

En lugar de un único umbral fijo, muchos equipos usan backoff progresivo: el primer bloqueo dura 15 minutos, el segundo 1 hora, el tercero 24 horas, y así sucesivamente. Esto castiga más a quien insiste con un patrón de ataque sostenido, sin inmovilizar para siempre a alguien que simplemente se equivocó un par de veces escribiendo su código.

Un detalle que se olvida seguido: el contador debe persistir entre despliegues y reinicios del servicio. Si vive solo en memoria de un proceso y el servicio reinicia por un deploy a mitad de un ataque, el atacante recibe un contador en cero como regalo. Usa un almacén compartido como Redis o la base de datos, nunca un Map en memoria de un único proceso en producción.

Se marca completado al leer el 80%.

Comentarios (2)

Se guardan solo en tu navegador.

  • Raúl Figueroa5 oct

    La race condition del contador la encontramos en producción con 40 requests paralelas. El callout de atomicidad es oro.

  • Javiera Olivares6 oct

    Me gustó el ejemplo de tres rondas, muestra perfecto por qué el contador por código no sirve.