Credential stuffing: por qué tu login parece sano y aun así filtra
El ataque que triunfa sin activar nunca un límite de peticiones: cómo se ve en tus registros, por qué los controles habituales lo pasan por alto y qué lo atrapa de verdad.
El ataque, y por qué no es fuerza bruta
El credential stuffing no adivina contraseñas. Las reproduce. Alguien toma pares de usuario y contraseña de la filtración de un servicio ajeno y prueba cada par una vez contra tu login, apostando a que una parte significativa de la gente reutiliza credenciales.
Esa única diferencia rompe casi todo lo que tienes desplegado. La fuerza bruta es ruidosa: muchos intentos contra una cuenta, fácil de limitar, fácil de bloquear. El stuffing es un intento por cuenta a lo largo de miles de cuentas, repartido durante horas y rotando direcciones.
La tasa de éxito es baja —se cita normalmente entre una décima y unos pocos por ciento— y es del todo suficiente. Contra una lista de un millón de pares, una décima de por ciento son mil cuentas funcionando.
Y el inicio de sesión exitoso es, según cualquier medida técnica disponible para tu aplicación, correcto. La contraseña era la buena. No hay nada que rechazar.
Cómo se ve en tus registros
La razón por la que esto pasa desapercibido durante semanas es que ninguna señal aislada resulta alarmante. El patrón solo existe en agregado.
- La tasa de fallos, no el número de fallos. Tu número absoluto de inicios fallidos puede apenas moverse, pero la proporción de fallos frente a aciertos se desplaza, porque los intentos se reparten entre cuentas que en su mayoría no existen o no coinciden.
- Un intento por cuenta. Un limitador indexado por cuenta no salta nunca. Uno indexado por dirección salta solo si el atacante olvidó rotar.
- Distribución de direcciones. Los intentos llegan desde pools de proxies residenciales, es decir, direcciones que parecen clientes corrientes y no un centro de datos.
- Ritmos demasiado regulares. El tráfico humano de inicio de sesión sigue el ritmo diario de tus clientes. Una tanda de stuffing es plana durante toda la noche.
- Un repunte silencioso de aciertos desde direcciones desconocidas. Esta es la que importa y sobre la que nadie alerta, porque un inicio de sesión exitoso no es un error.
Por qué los controles habituales lo pasan por alto
Cada uno de estos merece existir y ninguno detiene este ataque por sí solo.
El límite de peticiones está indexado por lo que no toca. Los límites por cuenta no saltan nunca con un intento por cuenta. Los límites por dirección se derrotan rotando por un pool de proxies residenciales, que es un servicio de consumo masivo.
El bloqueo de cuentas aquí es peor que inútil y activamente peligroso. No puede saltar con un solo intento y, en un login público, permite que cualquiera desactive las cuentas de tus clientes a voluntad.
Los CAPTCHA son un coste por intento que el atacante ya presupuestó. Los servicios de resolución los cobran por millar, y con un 0,1 % de éxito las cuentas siguen saliendo cómodamente.
Las reglas de complejidad de contraseñas no hacen nada. La contraseña reproducida ya es válida: cumple las reglas que impuso el otro servicio y probablemente también las tuyas.
La autenticación multifactor es el único control que rompe el ataque de verdad, y merece cualquier esfuerzo de despliegue. Tampoco ayuda en las cuentas que no la han activado, que en un producto de consumo son la mayoría.
Qué lo atrapa de verdad
Como el ataque es invisible en cualquier petición aislada, la detección tiene que trabajar al nivel de la sesión y de la dirección.
Señales a nivel de sesión. Una tanda de stuffing automatiza el formulario: los campos se rellenan sin el ritmo de la escritura, el foco se mueve de una forma que una mano humana no produce, el envío llega más rápido de lo que una persona podría haber tardado plausiblemente. Es la misma capa de comportamiento que lee cualquier detección invisible, y se ve ya en el primer intento.
Huellas de transporte. La herramienta tiene que abrir una conexión TLS, y su pila se nota. Una huella JA3/JA3N que no encaja con el navegador que la sesión dice ser es una señal fuerte, y el atacante no puede arreglarlo editando una cabecera.
Reputación a lo largo de los intentos. Un intento por cuenta no te da nada por cuenta, pero la misma dirección haciendo un intento contra cuatrocientas cuentas distintas es inequívoca. Ese agregado solo existe si algo está puntuando la dirección en vez de contar peticiones.
Reputación entre inquilinos. Las listas de stuffing se reproducen contra muchos sitios en la misma campaña. Una dirección que anoche pasó una lista contra el login de otro merece detenerse antes de que empiece con el tuyo, lo que solo es posible si ese conocimiento se comparte.
Una configuración defendible
Ordenado por cuánto decide cada paso del resultado.
Despliega MFA e impulsa su activación. Nada más de esta lista rompe el ataque por sí solo. Hazlo el ajuste por defecto de las cuentas nuevas y pídeselo a las existentes tras un inicio de sesión exitoso desde una dirección nueva.
Alerta sobre inicios de sesión exitosos desde direcciones desconocidas. Es barato, usa datos que ya tienes y es la señal de que una tanda de stuffing ha empezado a funcionar.
Puntúa la sesión, no la petición. Las señales de comportamiento y de transporte atrapan la automatización en el primer intento, antes de que el patrón agregado haya tenido tiempo de formarse.
Aplica en la pasarela. Una decisión aplicada en el JavaScript de la página es una decisión que la herramienta del atacante nunca ejecuta.
Comprueba las credenciales contra filtraciones conocidas, al establecerlas y al iniciar sesión. Si la contraseña reproducida ya está en un corpus público, puedes rechazarla antes de que se convierta en el problema de nadie.
Mantén tu lista de permitidos al día. Tu propia automatización de QA y tus pruebas de carga se parecen exactamente a este ataque, y bloquearlas un día de despliegue es un incidente de otro tipo.
Dónde encaja Karma
Karma cubre los puntos tercero y cuarto: puntúa cada sesión con señales de comportamiento y de transporte, mantiene una base de reputación por inquilino para que una dirección repartida entre muchas cuentas se vea como un solo actor, y ofrece una lista de bloqueo compartida opcional para que una lista que se está reproduciendo por todo el sector se conozca antes de llegar a tu login.
El veredicto va a tu pasarela, que detiene la petición antes de que tu aplicación llegue siquiera a evaluar la contraseña, así que el intento no te cuesta nada y nunca aparece como un inicio de sesión exitoso que investigar.
No sustituye a la MFA, y esta página no sugiere que lo haga. La MFA es el control que deja sin valor una contraseña reproducida; todo lo de aquí va de detener el tráfico que está probando qué contraseñas siguen funcionando.