saltar al contenido
volver al mapa
técnica
etapa: code
3 min de lectura

A07 Authentication Failures

El login es la puerta de entrada. Los intentos ilimitados, las contraseñas reutilizadas, las sesiones que nunca expiran y los tokens que no se verifican de verdad dejan que un atacante entre como un usuario real.

Hydra / ffuf (on systems you own)Have I Been Pwned Passwords APIjwt.io / jwt_toolPasskeys / WebAuthnOWASP ZAPKeycloak / Auth0 / Cognito

Qué es

Los fallos de autenticación son debilidades al confirmar la identidad de un usuario o al gestionar la sesión que sigue: permitir y fuerza bruta, aceptar contraseñas débiles o filtradas, exponer ids de sesión, omitir o verificar mal los tokens, u ofrecer un flujo de recuperación débil.

El :2025 lo lista como A07 con el nombre Authentication Failures.

Cómo funciona el ataque

  • Credential stuffing: listas de pares de correo y contraseña filtrados de otros sitios se reutilizan a escala contra tu login.
  • Fuerza bruta y password spraying: unas pocas contraseñas comunes en muchas cuentas, por debajo de cualquier bloqueo ingenuo.
  • Manejo débil de JWT: el servidor acepta alg: none, nunca verifica la firma ni la expiración, o usa un secreto fácil de adivinar.
  • y robo de sesión: el id de sesión no se rota después del login, viaja en la URL, o la cookie no tiene HttpOnly ni Secure.
  • Bypass de la recuperación de cuenta: enlaces de restablecimiento que nunca expiran o preguntas de seguridad con respuestas públicas.

Código vulnerable

Sin throttling, una comparación rápida de un hash sin salt, un id de sesión conservado después del login y un token que se decodifica pero nunca se verifica.

login.js (Express)javascript
app.post("/login", async (req, res) => {
  const user = await db.user.findByEmail(req.body.email);
  if (user && user.passwordHash === sha1(req.body.password)) {
    req.session.userId = user.id;          // mismo id de sesión que antes del login
    return res.json({ ok: true });
  }
  res.status(401).json({ error: "wrong email or password" });   // intentos ilimitados
});

const claims = jwt.decode(token);          // decode() NO verifica la firma
tokens.pypython
claims = jwt.decode(token, options={"verify_signature": False})

La corrección

Aplica throttling por cuenta y por IP, verifica las contraseñas con un hash lento, regenera la sesión en el login y define las flags de la cookie. Verifica los tokens con un algoritmo fijado, una clave real y comprobaciones de emisor, audiencia y expiración. Cuando puedas, usa passkeys o un proveedor de identidad en lugar de construir el login tú mismo, y agrega un segundo factor para las cuentas sensibles.

login.js (Express)javascript
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 10, keyGenerator: (r) => `${r.ip}:${r.body?.email}` });

app.post("/login", limiter, async (req, res) => {
  const user = await db.user.findByEmail(req.body.email);
  const ok = user && (await argon2.verify(user.passwordHash, req.body.password));
  if (!ok) return res.status(401).json({ error: "wrong email or password" });   // el mismo mensaje en ambos casos
  req.session.regenerate(() => {         // id nuevo tras el login: derrota la fixation
    req.session.userId = user.id;
    res.json({ ok: true });
  });
});

app.use(session({ cookie: { httpOnly: true, secure: true, sameSite: "lax", maxAge: 3600_000 } }));
tokens.py (PyJWT)python
claims = jwt.decode(
    token,
    key=PUBLIC_KEY,
    algorithms=["RS256"],          # fija el algoritmo; nunca aceptes "none"
    audience="api.example.com",
    issuer="https://id.example.com",
    options={"require": ["exp", "iat", "sub"]},
)
SecurityConfig.java (Spring Security)java
http.sessionManagement(s -> s.sessionFixation().migrateSession())
    .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()));   // verifica firma, exp y emisor mediante configuración

Demuestra la corrección

Prueba los comportamientos de los que depende un atacante: el undécimo intento se rechaza, el id de sesión cambia tras el login y los tokens manipulados o sin firma se rechazan.

test_auth.py (pytest)python
def test_login_is_throttled(client):
    codes = [client.post("/login", json={"email": "a@x.io", "password": f"guess{i}"}).status_code for i in range(15)]
    assert 429 in codes

def test_session_id_changes_after_login(client, user):
    before = client.get("/").cookies.get("sid")
    client.post("/login", json=user.credentials)
    assert client.cookies.get("sid") != before

def test_unsigned_token_is_rejected(client):
    forged = b64(b'{"alg":"none"}') + "." + b64(b'{"sub":"admin"}') + "."
    assert client.get("/api/me", headers={"Authorization": f"Bearer {forged}"}).status_code == 401

def test_cookie_flags(client, user):
    cookie = client.post("/login", json=user.credentials).headers["set-cookie"]
    assert "HttpOnly" in cookie and "Secure" in cookie

Checklist

  • Aplica y monitorea el login por cuenta, por IP y por dispositivo; agrega retraso progresivo o desafío, y no solo bloqueo total (que habilita la denegación de servicio).
  • Verifica las contraseñas nuevas contra una lista de contraseñas filtradas; exige longitud en lugar de reglas arbitrarias de complejidad.
  • Ofrece autenticación multifactor, idealmente resistente al phishing (passkeys, ), y exígela a los administradores.
  • Rota el id de sesión en el login y en los cambios de privilegio; hazlo expirar; invalídalo al cerrar sesión.
  • Verifica cada token: firma, algoritmo, emisor, audiencia, expiración. Nunca confíes solo en el decode.
  • Haz la recuperación tan fuerte como el login: enlaces de un solo uso y corta duración; la misma respuesta genérica para correos conocidos y desconocidos.
Credential stuffingAtaque que prueba en masa pares usuario/contraseña filtrados de otros servicios. Firma: muchos fallos de inicio de sesión distribuidos en muchas IPs, con pocos intentos por cuenta.ver en el glosario →OWASP Top 10La lista de referencia más conocida de riesgos de seguridad en aplicaciones web (A01 control de acceso roto, A02 fallas criptográficas, A03 inyección...), revisada cada pocos años por la comunidad OWASP. Es un mapa educativo de categorías de riesgo, no un checklist de herramientas ni una certificación.ver en el glosario →Session fixationUn atacante coloca un id de sesión conocido en el navegador de la víctima; si la app conserva ese id después del login, el atacante comparte la sesión. Regenera el id al iniciar sesión.ver en el glosario →Rate limitingLímite de solicitudes por entidad y ventana de tiempo. La elección de la clave (IP, cuenta, token, ASN) pesa tanto como el valor del límite.ver en el glosario →PasskeyWebAuthnUn inicio de sesión resistente al phishing basado en un par de claves guardado en el dispositivo del usuario; el sitio solo conserva la clave pública, así que no hay contraseña que robar ni reutilizar.ver en el glosario →

conexiones en el mapa

actualizado el 14/10/2026 · fuentes para profundizar · reportar un error

en otros dominios

  • Bot managementsecopsel credential stuffing es, ante todo, tráfico automatizado

Desbloquea

pon a prueba lo que entendiste

Cargando preguntas…