Panorama#
Los dos capítulos web anteriores atacan en direcciones opuestas: la inyección rompe el contexto de datos del servidor (ver 3.2 · Inyección), y la familia del lado cliente usa la sesión de otro usuario como ariete (ver 3.3 · Cliente). Este mira al corazón de la aplicación: la maquinaria con la que decide quién es cada visitante y qué le permite hacer. No hay un intérprete que engañar ni una víctima externa; el objetivo es la lógica de identidad de la propia aplicación, la que le concede a un usuario una sesión y la arrastra de un request al siguiente.
Bajo la variedad de técnicas hay una sola idea: en cada request, una aplicación responde tres preguntas encadenadas, y cada respuesta descansa en la anterior.
- ¿Quién es? — la autenticación establece la identidad una vez, en el login.
- ¿Sigue siendo el mismo? — la gestión de sesión transporta esa identidad a través de un protocolo (HTTP) que es stateless y no recuerda nada por sí mismo.
- ¿Puede hacer esto? — el control de acceso verifica, en cada acción, que la identidad establecida tiene permiso para lo que pide.
Son eslabones de una cadena, no defensas paralelas. Una gestión de sesión rota saltea la autenticación más robusta: si el atacante secuestra el token, nunca necesita la contraseña. Un control de acceso roto desperdicia las dos: la app sabe perfectamente quién es el usuario y aun así lo deja tocar lo ajeno. Por eso invertir en un login fuerte y descuidar el control de acceso es tirar el esfuerzo; la cadena cede por su eslabón más débil.
Sobre esos tres eslabones corre un cuarto elemento que no es un eslabón sino el tejido conectivo: la lógica de
negocio. Los logic flaws no tienen firma —no hay un <script> ni una comilla que buscar— y cada uno es único,
pero todos nacen de la misma raíz: el usuario controla cada aspecto de cada request. Puede cambiar un valor,
mandar un parámetro que no le pidieron, y —lo que más se olvida— omitir un parámetro. Cada asunción que el
desarrollador hace sobre lo que el cliente “va a mandar” es una asunción que el atacante puede violar, y ahí es
donde los tres eslabones se rompen en la práctica.
flowchart LR
REQ["Request del usuario\n(controla cada aspecto)"] --> A{"¿Quién es?"}
A -->|"autenticación"| S{"¿Sigue\nsiendo el mismo?"}
S -->|"gestión de sesión"| AC{"¿Puede\nhacer esto?"}
AC -->|"control de acceso"| OK["Acción ejecutada"]
A -.->|"brute-force · enumeración ·\nfail-open · multistage"| BREAK["Eslabón roto →\ncompromiso"]
S -.->|"token con significado /\npredecible · secuestro · fixation"| BREAK
AC -.->|"IDOR · método HTTP ·\nadmin=true · multietapa"| BREAK
LOGIC["Logic flaw\n(asunción violada)"] -.-> A
LOGIC -.-> S
LOGIC -.-> ACLas cuatro secciones que siguen recorren los eslabones en orden y cierran con la lente púrpura que los une: la detección es difícil justamente porque estos requests parecen legítimos —vienen autenticados y bien formados—, así que la defensa primaria es estructural y la telemetría caza patrones de negocio, no firmas de ataque. Todo se apoya en el mapa de superficie de 3.1 · Metodología: cada función de identidad, cada parámetro que fija un privilegio y cada identificador de recurso es un punto a probar.
Autenticación: el eslabón más débil#
La autenticación es la primera línea de defensa y, en aplicaciones reales, el eslabón que más cae. Es alcanzable de forma anónima y, si se rompe, suele entregar el control completo. La metodología tiene un lema que resume todo: mirar en todos lados. El login principal es solo una de las superficies; el defecto que se evita ahí reaparece en el registro, el cambio de contraseña, el remember me, la recuperación y la impersonación.
Defectos de diseño#
Brute-force del login. Se explota distinguiendo el éxito del fracaso —por el código HTTP, la longitud de la
respuesta o la ausencia del mensaje “login incorrect”— y ordenando el ataque para no dispararse el bloqueo de
cuenta. Contra varios usuarios, la clave es atacar en anchura (breadth-first): iterar la lista de contraseñas
(las más comunes primero) probando cada una sobre todos los usernames, en vez de agotar un usuario antes de
pasar al siguiente. Encuentra las cuentas de contraseña común más rápido y espacia los intentos por cuenta, evitando
el lockout. Si el contador de fallos vive en el cliente (una cookie failedlogins=1) se saltea sin más; si vive
en la sesión, basta pedir una sesión fresca. Y aun con lockout, si la app responde distinto a la contraseña
correcta sobre una cuenta bloqueada, la adivinación continúa: se descubre el par válido y se espera el auto-unlock.
Enumeración de usuarios. Un mensaje verboso (“usuario inexistente” vs “contraseña incorrecta”) convierte el login en un oráculo: un ataque automatizado marca los usernames válidos de una lista. Aparece también en el registro (si rechaza duplicados, revela los existentes), el cambio de contraseña y la recuperación. Aun con mensajes “idénticos” puede haber diferencias sutiles —de status HTTP, de tipografía, escondidas en comentarios del HTML— que Burp Comparer resalta. Y aunque las respuestas sean idénticas byte a byte, el tiempo delata: un username válido dispara procesamiento back-end (lookup en base de datos, cálculo de hash) que uno inválido no.
Transmisión insegura de credenciales. Más allá del HTTP plano que un eavesdropper intercepta, las credenciales se filtran por vías que sobreviven a HTTPS: en el query string (quedan en el historial del navegador y los logs de servidor y proxies), en cookies replayables, o —el caso más sutil— cuando la página de login se carga por HTTP y solo cambia a HTTPS al enviar el formulario. Un atacante posicionado modifica el destino del formulario a HTTP antes de que la víctima escriba nada; el usuario no puede verificar la autenticidad de una página que llegó sin cifrar. Lo correcto es servir el formulario de login ya por HTTPS.
Las funciones periféricas reintroducen todo#
El error recurrente: la vulnerabilidad evitada con cuidado en el login principal reaparece, sin vigilancia, en las funciones de alrededor.
Cambio de contraseña: a menudo accesible sin autenticar, con mensaje verboso de username y adivinación ilimitada de la “contraseña actual”. Si el formulario autenticado no tiene campo username, se puede inyectar uno con el mismo nombre que en el login para pisar la identidad de la sesión y brute-forcear a otro usuario.
username=victima&existingPassword=§FUZZ§&newPassword=x&confirm=xRecuperación de contraseña olvidada: suele ser el eslabón más débil de todos. El desafío secundario (apellido materno, color favorito) tiene un espacio de respuestas mucho menor que una contraseña y es información pública o auto-elegida y trivial; los hints son peor. Y el mecanismo de re-acceso falla de varias formas: revelar la contraseña vieja (acceso indefinido, sin detección), dejar caer directo a una sesión autenticada, o enviar la URL de reset a un email que viene en un campo oculto modificable (cambiarlo por el del atacante). Las recovery URL con patrón se predicen registrando varias cuentas seguidas y observando la secuencia.
“Remember me”: la variante ingenua guarda
RememberUser=daf(el username en claro) en una cookie persistente y crea sesión sin login —con una lista de usernames se entra a cualquier cuenta—. Aun cuando guarda un identificador de sesión persistente, es atacable si el identificador es predecible, y siempre lo roba un XSS (ver 3.3 · Cliente).Impersonación (típica en helpdesk de banca): falla si es una función “oculta” sin control de acceso (
/admin/ImpersonateUser.jsp), si confía en un dato controlable (una cookie que dice qué cuenta usa la sesión), o si se implementa como un backdoor password que valida con cualquier username. Ese backdoor se delata en un brute-force porque aparece como un segundo hit —dos contraseñas válidas para una cuenta, o una contraseña que matchea varios usernames—.
Validación incompleta. Algunas apps truncan la contraseña, la comparan case-insensitive o eliminan caracteres “raros” antes de validar. Cada limitación reduce el keyspace en un orden de magnitud. Se detecta experimentando con la propia cuenta —quitar el último carácter, cambiar mayúsculas, sacar especiales— y, si algo funciona, se afina el ataque automatizado eliminando casos superfluos.
Defectos de implementación#
Fail-open. El logic flaw devastador de la autenticación: un catch que traga la excepción y deja el flujo
caer en el camino “autenticado”.
try {
User user = db.getUser(uname, passwd);
if (user == null) { session.setMessage("Login failed."); return doLogin(session); }
} catch (Exception e) {} // ← traga la excepción
session.setMessage("Login successful."); return doMainMenu(session); // ← se ejecuta si getUser() lanzó
Si getUser lanza (por ejemplo, un null pointer porque falta el parámetro), el flujo esquiva el if (user==null)
y ejecuta el return doMainMenu() —sesión iniciada sin credenciales—. En código simple no pasaría una revisión,
pero en mecanismos complejos con estado del login distribuido en capas, el mismo defecto conceptual es probable. Se
caza repitiendo el login válido y mutando cada parámetro y cookie: string vacío, quitar el par nombre/valor,
valores muy largos o cortos, cambiar números por strings —y observando cualquier divergencia respecto del caso base—.
Logins multietapa. Diseñados para más seguridad (username y contraseña → dígitos de un PIN → token físico), suelen ser menos seguros por errores de implementación. Los supuestos peligrosos:
- Asumir que quien llega a la etapa 3 pasó la 1 y la 2 → ir directo de la 1 a la 3 autentica con credencial parcial.
- Confiar en la etapa 2 datos que se validaron en la 1 pero viajan manipulables (flags como “es admin” o “necesita más etapas”) → pisarlos para elevar.
- No verificar que sea la misma identidad en cada etapa → dar username y contraseña de Alice en la etapa 1 y username y token de Bob en la 2, autenticando como cualquiera de los dos (basta tener el token propio y descubrir la contraseña ajena).
- Confiar flags de progreso viajados por el cliente (
stage2complete=true) → saltar etapas.
La pregunta aleatoria (dos letras de una palabra secreta) se rompe si la pregunta se guarda en un campo oculto o cookie —el atacante elige cuál responder— o si se re-randomiza en cada intento fallido: el atacante cicla hasta que salga la que sabe, sin riesgo de lockout. Es sutil y muy común.
Almacenamiento inseguro. Guardar en claro o con un hash sin salt (MD5/SHA-1) permite el lookup del hash en bases precomputadas. Como la cuenta de base de datos de la app tiene lectura y escritura sobre las credenciales, una SQLi o command injection las expone (ver 3.2 · Inyección) —el cruce directo entre este eslabón y el del servidor—.
Gestión de sesión: las llaves del reino#
Tras el login, la garantía de identidad persiste en un token, casi siempre en cookie, que el cliente reenvía en cada request porque HTTP no recuerda nada. Si ese token cae, se saltea toda la autenticación y se secuestra la sesión de otro usuario —comprometer un administrador equivale a comprometer la aplicación—. Las vulnerabilidades caen en dos categorías: cómo se genera el token y cómo se maneja.
flowchart TD
T["Token de sesión capturado"] --> Q1{"¿Decodifica a algo\nlegible? (hex/Base64)"}
Q1 -->|"sí"| MEAN["Token con significado\n→ editar user/rol/uid\ny adivinar sesiones ajenas"]
Q1 -->|"no"| Q2{"¿Serie de tokens\nrevela un patrón?"}
Q2 -->|"secuencia / tiempo / RNG débil"| PRED["Token predecible\n→ extrapolar pasados y futuros\n(Sequencer para medir)"]
Q2 -->|"salta 8/16 bytes\nal alargar el input"| ENC["Token cifrado con significado\n→ ECB reorder / CBC bit-flip /\nencryption oracle"]
Q2 -->|"parece robusto"| HANDLE["Atacar el MANEJO:\nred · logs/URL · fixation ·\nscope liberal · XSS que lo roba"]Generación: cuando el token no es aleatorio#
Token con significado. Lo que parece aleatorio suele ser una codificación. 757365723d... es solo hex →
decodifica a user=daf;app=admin;date=10/09/11. Descubierta la estructura (username, id numérico, rol, timestamp,
IP), se generan tokens para usernames comunes o enumerados y se prueban contra una página session-dependent. Mutar
el token byte a byte (el char frobber de Intruder) revela qué subcomponentes se ignoran, reduciendo la entropía
efectiva.
Token predecible. Viene de tres fuentes. Una secuencia oculta se revela decodificando por capas: una serie
de tokens Base64 → binario → hex → restar cada valor del anterior expone un delta constante (0x97C4EB6A), y con
eso se reproducen los pasados y futuros. Una dependencia temporal: tokens 3124538-1172764258718 (índice
incremental más milisegundos), delatados porque el segundo número salta cientos de miles aunque se hayan emitido
pocos tokens. Un RNG débil: java.util.Random es un generador congruencial lineal extrapolable en ambas
direcciones desde un solo output —el bug de Jetty por el que “un token entrega todos los presentes y futuros”—.
Burp Sequencer mide la aleatoriedad reportando la entropía efectiva en bits, con dos salvedades que importan:
lo determinista puede pasar los tests (un LCG o un hash de un contador producen output que pasa, pero quien
conoce el algoritmo extrapola con certeza), y los bits que fallan pueden no ser predecibles en la práctica.
Token cifrado con significado. Cifrar un token estructurado parece robusto, pero el modo del cifrador abre
ataques sin conocer la clave. En ECB, bloques de plaintext idénticos dan ciphertext idéntico, así que se
pueden reordenar o duplicar bloques para modificar el plaintext de forma coherente: registrando un username con
un valor numérico en el offset justo (daf1) y duplicando ese bloque se pisa el uid, y ciclando usernames se
recorre todo el rango. En CBC el encadenamiento impide ese patrón, pero permite bit-flipping: modificar un
bloque de ciphertext lo corrompe pero altera de forma controlada el plaintext del bloque siguiente (8
requests por byte con el bit flipper de Intruder). Y si la app tiene una función que revela el plaintext de un
string cifrado arbitrario —una encryption oracle de tipo “reveal”—, el bit-flipping deja de ser ciego.
Manejo: cuando el token perfecto se filtra igual#
Aun con un token impecable, el manejo descuidado abre el secuestro:
- Exposición en la red: token en claro por HTTP, o por HTTPS que revierte a HTTP (Firesheep capturaba la
cookie), o emitido en la primera página pre-login y no renovado al autenticar, o filtrado por mixed content
(un
<img>por HTTP en una página HTTPS ya transmitió la cookie). La cura: cookie con secure flag y HTTPS en toda la app. - Exposición en logs: el vector masivo es el token en la URL (
jsessionid—inurl:jsessioniden Google devuelve miles). Aparece en logs de navegador, servidor y proxies, y en los Referer logs de sitios externos: en una webmail que pone el token en la URL, el atacante manda un email con un enlace a su servidor y, al abrirlo, el token llega en elRefereren tiempo real. El secuestro se materializa inyectando unSet-Cookiecon el token capturado en la próxima respuesta. - Mapeo defectuoso: sesiones concurrentes (permiten usar credenciales robadas sin detección), tokens
estáticos (el mismo reemitido siempre, que no son sesiones), y —el cruce con el próximo eslabón— un token
user=daf;r1=<random>donde elrandomes impredecible pero eluseres directamente controlable es un access control flaw: basta unr1válido y cualquieruser. - Terminación defectuosa: sin expiración server-side, sin logout real, o un logout que solo borra la cookie del cliente mientras el servidor sigue aceptando el token. Lo único que importa es la invalidación server-side.
- Scope liberal de cookies:
domain=wahh-app.commanda la cookie a todos los subdominios —un blog enherman.wahh-blogs.comcon JavaScript propio roba el token de la app principal—. El scope de cookie ignora protocolo y puerto (es menos estricto que la same-origin policy), y el scope por path es inefectivo como control (un iframe cross-path lo lee). La cura: scope tan restrictivo como se pueda y un hostname dedicado para la app que autentica.
Control de acceso: la clase más común#
El control de acceso decide, apoyado en los dos eslabones previos, si un request puede realizar la acción o tocar el recurso que pide. Es la clase de vulnerabilidad más común —los autores de WAHH la hallaron en el 71% de las aplicaciones testeadas— porque el chequeo debe repetirse en cada request sobre cada recurso, y es una decisión de diseño humana que ninguna tecnología resuelve sola. Tres categorías:
- Vertical: entre roles (usuario vs administrador).
- Horizontal: entre recursos del mismo tipo (tu email vs el ajeno).
- Context-dependent: el orden de un proceso multietapa (pagar antes de enviar).
Y sus tres ataques correspondientes: escalada vertical, escalada horizontal y explotación de la lógica de negocio. Lo horizontal suele derivar en vertical —poder fijar la contraseña de otro escala a tomar un administrador—.
El catálogo de defectos#
Funcionalidad “protegida” solo por no linkearla. La falla más burda y grave: la app controla el acceso a
/admin/ ocultando el enlace, o con una URL críptica (/menus/secure/ff457/DoAdminMenu2.jsp) confiando en que
no se adivine. Pero las URLs no son secretos: quedan en historiales y logs, no rotan como las contraseñas, y
—clave— se filtran en el código cliente, donde un if (isAdmin) { adminMenu.addItem("/menus/secure/...") } en
el JavaScript expone las rutas administrativas a cualquiera que lea la fuente.
Acceso directo a métodos e IDOR. Cuando una URL es la invocación directa de un método de API
(.../getCurrentUserRoles), invita a probar los vecinos (getAllUsers, getAllRoles), y muchas APIs mapean todo
por defecto. La forma más común es la función identifier-based (IDOR, Insecure Direct Object Reference):
https://wahh-app.com/ViewDocument.php?docid=1280149120 # iterar docid → ver documentos ajenos
https://wahh-app.com/MyAccount.php?uid=1241126841 # cambiar uid al de otro usuario
Los identificadores no son secretos: la app es vulnerable aunque sean GUID aleatorios (si no controla el acceso), y trivial si son secuenciales. Los logs de la app son una mina de identificadores válidos.
Bypass de proceso multietapa. El defecto: proteger la primera etapa y asumir que quien llega a las siguientes está autorizado. En una transferencia bancaria que valida la cuenta origen en la etapa 1 y la pasa por campos ocultos, si el POST final no revalida, interceptarlo y cambiar la cuenta origen ejecuta una escalada horizontal —transferir desde la cuenta de otro—.
Archivos estáticos y misconfiguración de plataforma. Un recurso estático servido directo
(/download/9780636628104.pdf, un ISBN) no ejecuta código de aplicación, así que no puede chequear privilegios:
se itera el esquema de nombres y se baja todo. Y las reglas tipo firewall por método HTTP fallan si no son
default-deny:
POST /admin/createUser ... # denegado por la regla de plataforma
GET /admin/createUser?user=x... # mismo handler, params en el query string → bypass
HEAD /admin/createUser?user=x... # ejecuta el handler de GET (sin body, no importa)
FOO /admin/createUser?user=x... # método inválido → algunos servers lo pasan a GET
Modelos basados en datos del cliente. Inseguros por diseño: parameter-based (home.jsp?admin=true, leído en
cada request), Referer-based (permitir una función si viene “referida” del menú admin —el Referer es totalmente
controlable—) y location-based (geolocalización por IP, evadible con un proxy que termine en la ubicación exigida).
Cómo se testea#
El método más efectivo es acceder con distintas cuentas. Para lo vertical, mapear todo con una cuenta poderosa y
reintentar con una de menor privilegio (Burp compare site maps: una respuesta idéntica a la de la función
administrativa señala la vulnerabilidad). Para lo horizontal, dos cuentas del mismo nivel. El ataque más rentable es
la cosecha por identificador: si una página de información de cuenta muestra el username y la contraseña
(enmascarada en pantalla pero enviada al navegador), iterar el rango de uid cosecha las credenciales de todos, y
los identificadores más bajos suelen ser los administradores. Es el mismo IDOR llevado a la escala industrial.
Lógica de negocio: el tejido conectivo#
Los logic flaws no tienen firma reconocible: cada instancia es única, ningún scanner los detecta, y su rasgo definitorio es una asunción defectuosa del desarrollador —“si pasa A, entonces B, así que hago C”, sin preguntarse “¿y si pasa X?"—. Como detectarlos exige pensamiento lateral, no se eliminan con estándares de desarrollo ni pentests normales. La raíz común es la que atraviesa todo el capítulo: el usuario controla cada aspecto de cada request, incluida la potestad de omitir un parámetro, no solo de cambiar su valor.
Los tres tests base que destapan la mayoría:
- Quitar cada parámetro por turno (el nombre y el valor, no dejarlo vacío —el servidor suele manejarlo distinto—), uno por vez, para alcanzar todos los code paths.
- Forced browsing: mandar los requests de un proceso multietapa fuera de secuencia —saltar etapas, repetir una, acceder a una temprana después de una tardía—.
- Cruzar parámetros de etapa: mandar un parámetro de una etapa en otra.
Sobre eso, el pensamiento lateral: meterse en la cabeza del desarrollador e imaginar qué asunción hizo. Los patrones recurrentes, cada uno una asunción distinta violada:
- Encryption oracle por clave reutilizada. Una app cifraba el token
RememberMecon 3DES robusto y decidió cifrar también elScreenNamecon la misma clave por ser “menos valioso”. Pero el usuario elige su screen name y lo ve: poner como screen nameadmin|1|<IP>hace que la app lo cifre, y submitir esa cookie comoRememberMela descifra y loguea comouid=1. La app se volvió un oráculo de cifrado. - Inferir el rol por presencia/ausencia de un parámetro. Un cambio de contraseña compartía el script entre
usuarios (que mandan
existingPassword) y administradores (que no):if (null == existingPassword) return true;—tratado como admin—. Omitir el parámetro resetea la contraseña de cualquiera. Es el mismo fail-open de la autenticación, visto desde la lógica. - Forced browsing a la etapa de pago. Un checkout de cuatro etapas asumía el recorrido en orden; ir directo de la etapa 2 a la 4 genera una orden finalizada para envío sin pagar.
- Estado actualizado por cualquier parámetro fuera de secuencia. Un flujo que actualizaba el estado con cada
par nombre/valor del POST permitía saltear validaciones, fijar un precio arbitrario (
premium/valuefuera de secuencia) y —adivinando el nombre del parámetro que usa el underwriter— aceptar la propia solicitud. - Reuso de un objeto de sesión que pisa la identidad. Un registro reusaba la clase que guardaba el cliente en la sesión y que también servía al núcleo autenticado: submitir en el registro los datos de otro cliente sobrescribía la identidad de la sesión → acceso a la cuenta ajena.
- No rechazar números negativos. Un ERP exigía aprobación para transferencias
> $10.000:if (amount <= threshold) return false;. Un negativo siempre pasa el chequeo, y el módulo bancario lo procesa como un positivo en dirección opuesta → un transfer de −$20.000 de B a A equivale a +$20.000 de A a B, sin aprobación. - Descuento antes de finalizar el carrito. Aplicar el descuento por bundle al agregar ítems: agregar todos los productos para el descuento máximo y remover los no deseados deja el descuento sobre lo que queda.
- No escapar el carácter de escape, e interacción de filtros. Un anti-command-injection escapaba los
metacaracteres con
\pero no escapó el\mismo:foo\;ls→foo\\;ls→ el shell ve el;sin escapar y ejecuta. Y dos filtros que se pisan reabren la vulnerabilidad: doblar comillas (anti-SQLi) y truncar a 128 caracteres —mandar 127amás una comilla se dobla a''y luego se trunca a una comilla sola, no balanceada—, reabriendo la SQLi de 3.2. - Storage estático de datos por-usuario. Debug messages con identidad y token guardados en un contenedor estático (no por sesión) permiten cosechar credenciales ajenas por polling de la URL de error; y un identificador de usuario guardado en una variable estática entre escritura y lectura produce una race condition bajo concurrencia —un usuario cae en la sesión de otro—, invisible en un pentest normal pero visible en revisión de código.
Defensa y detección: la lente púrpura#
Los cuatro eslabones comparten un rasgo que define su defensa: el request malicioso parece legítimo —viene autenticado y bien formado, sin payload reconocible—. El control de acceso roto y los logic flaws, sobre todo, son casi indetectables por firma. Por eso la prevención es estructural y la telemetría caza patrones de negocio, no ataques. La misma lógica de correlación de 4.9 · Defensa de AD aplica a la capa web.
Endurecimiento (prevención estructural)#
| Eslabón / amenaza | Control primario |
|---|---|
| Brute-force / enumeración | Respuestas genéricas (mismo mensaje, status y timing para usuario y contraseña inválidos); lockout que rechaza la cuenta suspendida sin chequear credenciales ni revelar que está suspendida; CAPTCHA; contraseñas fuertes y usernames de alta entropía |
| Transmisión / periféricas | Formulario de login servido por HTTPS; solo POST, nunca credenciales en URL/cookie; recuperación por recovery URL única, time-limited y single-use al email registrado + aviso out-of-band; validación completa (case-sensitive, sin truncar) |
| Fail-open / multietapa | Catch-all de excepciones que invalida la sesión; estado del multietapa solo server-side, verificar todas las etapas previas y la misma identidad en cada una; pregunta aleatoria persistida en el perfil (no elegible) |
| Almacenamiento | Hash fuerte salteado por-cuenta (bcrypt/Argon2, o SHA-256+salt) para frustrar el lookup offline |
| Generación de token | Token sin significado ni estructura desde un CSPRNG; todos los datos de sesión server-side; renovar el token tras el login (anti-fixation) |
| Manejo de token | Secure/HttpOnly, nunca en la URL; logout que invalida server-side + expiración por inactividad; scope de cookie mínimo + hostname dedicado; auditar XSS; per-page tokens en apps críticas |
| Control de acceso | Componente central default-deny que valide cada request desde la sesión (nunca desde admin=true/Referer/IP ni la ignorancia de URLs); revalidar todo identificador del cliente; proteger estáticos vía página dinámica; reglas de plataforma default-deny por método+path+rol; modelo multicapa (cuentas de base de datos restringidas, OS least-privilege) para que un bypass en la capa app no alcance la acción |
| Lógica de negocio | Documentar y comentar todas las asunciones; decidir toda identidad/estado desde la sesión; storage por-usuario, nunca estático; rechazar negativos y canonicalizar límites; finalizar la orden antes del descuento; escapar el carácter de escape |
Detección (la señal en la telemetría)#
- Autenticación: velocidad de fallos, distribución de IPs (credential stuffing/password spraying), y éxito tras muchos fallos; un login con dos contraseñas válidas para una cuenta delata el backdoor de impersonación.
- Sesión: requests con tokens inválidos en volumen (adivinación/brute-force de tokens), reuso de un token desde una IP o User-Agent nuevos, y una sesión activa desde dos geolocalizaciones a la vez (secuestro).
- Control de acceso: una sola sesión iterando identificadores secuenciales (
docid=1000,1001,1002…) = cosecha/exfiltración; un usuario de bajo privilegio pegándole a una función administrativa; un método HTTP anómalo (HEAD/inválido) sobre endpoints sensibles. - Lógica de negocio: anomalías de negocio —montos negativos, parámetros que la UI no ofrece, etapas fuera de secuencia (pago saltado), descuentos sobre carritos modificados, ráfagas de búsquedas que barren términos (inferencia)—.
El emparejamiento púrpura es el mismo en los cuatro eslabones: el atacante opera con requests que parecen normales, así que el defensor no lo caza por una firma sino por el patrón —volumen y secuencia de identificadores, anomalía de valor o de flujo, origen o timing del login—. Eso exige logging rico de eventos de identidad y de negocio (intentos de login con username e IP, transiciones de estado, montos, decisiones de precio). La correlación de esas señales a escala de SIEM y el análisis forense de los logs web se desarrollan del lado azul en la Parte 5, y las defensas de las variantes modernas de estos eslabones —OAuth, JWT, deserialización— en 3.5 · Web moderno.
Referencias#
red-web/wahh/cap-03— Attacking Authentication: brute-force (distinguir éxito, breadth-first, lockout), enumeración de usuarios (mensaje verboso, Comparer, timing), transmisión insegura, funciones periféricas (cambio de contraseña, recuperación, remember me, impersonación), validación incompleta, fail-open, logins multietapa y almacenamiento inseguro, con la sección PURPLE más rica del libro (respuestas genéricas + lockout ciego + logging de anomalías).red-web/wahh/cap-04— Attacking Session Management: generación de tokens (con significado, predecibles por secuencia/tiempo/RNG débil, cifrados con ECB reorder/CBC bit-flipping/encryption oracle, Sequencer) y manejo (exposición en red/logs/URL, mapeo defectuoso, terminación, scope liberal de cookies), con la defensa por tokens desde CSPRNG, secure flag, invalidación server-side y per-page tokens.red-web/wahh/cap-05— Attacking Access Controls: vertical/horizontal/context-dependent, security by obscurity de URLs, IDOR y acceso directo a métodos, bypass de multietapa y de método HTTP, archivos estáticos, modelos basados en datos del cliente, cosecha por identificador, y la defensa por componente central default-deny y modelo multicapa.red-web/wahh/cap-08— Attacking Application Logic: los logic flaws como asunción no examinada, los tests base (quitar/forced browsing/cruzar parámetros) y los doce patrones reales (encryption oracle, inferir rol por omisión, forced browsing de pago, estado por parámetro, reuso de objeto de sesión, número negativo, descuento pre-finalización, escapar el escape, interacción de filtros, inferencia por búsqueda, storage estático).- OWASP — Authentication, Session Management, Authorization y IDOR Prevention Cheat Sheets.
- MITRE — CWE-287 Improper Authentication, CWE-384 Session Fixation, CWE-639 IDOR / Authorization Bypass, CWE-285 Improper Authorization y CWE-840 Business Logic Errors.