Cincuenta minutos en clase; los minutos de cada ejercicio son un ritmo sugerido y el docente gestiona el tiempo, así que lo que no se cierre en el aula continúa como trabajo independiente. Trabajan sobre la muestra de la tabla de usuarios del caso. Está anonimizada: los usuarios no existen y las contraseñas del ejemplo son las genéricas de manual, pero los resúmenes se comportan como los de la base de Coopaburrá.
head, tail y cat), no en cmd ni en PowerShell, donde no existen. En macOS o Linux, la Terminal normal. La alternativa en Python corre en cualquier terminal.Escriba en el primer recuadro y cambie una sola letra en el segundo. Los dos resúmenes no se parecen en nada: esa es la propiedad que hace útil a una función de resumen, y también la que hace inútil compararlas «por parecido».
Que el resumen cambie por completo con un solo bit no protege una contraseña. Quien quiera averiguarla no intenta parecerse: reproduce el cálculo con una candidata y compara. La única defensa real es que cada intento cueste tiempo, y eso no lo da la función de resumen: lo da el factor de trabajo de una función de derivación, que es el ejercicio 3.
La segunda defensa es que el resultado de un intento no sirva para dos cuentas. Eso lo da una sal distinta por usuario. Una sal constante para toda la base —como la que declara la documentación de Coopaburrá— no aporta eso: solo obliga a rehacer el cálculo una vez más, con esa sal.
A partir de la longitud del resumen, determinen qué función lo produjo. Justifiquen con el número de bits, no con la apariencia.
id usuario hash 1 usuario_a 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 2 usuario_b 7c4a8d09ca3762af61e59520943dc26494f8941b 3 usuario_c 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 sal declarada por la fábrica de software: constante "c00pab2019" para todos los registros
Las contraseñas del ejemplo son las dos más genéricas que existen, las de cualquier manual de seguridad. El objetivo no es adivinarlas: es comprobar en cuál de tres condiciones el resumen de la evidencia se reproduce, porque eso revela si la sal declarada se aplicó o solo está documentada.
El resumen de la candidata sola, sin ninguna sal.
El resumen de la candidata con la sal declarada detrás.
El resumen de la candidata con la sal declarada delante.
echo -n "password" | openssl dgst -sha1 echo -n "passwordc00pab2019" | openssl dgst -sha1 echo -n "c00pab2019password" | openssl dgst -sha1
import hashlib objetivo = "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8" # usuario_a y usuario_c candidata = "password" sal = "c00pab2019" sin_sal = hashlib.sha1(candidata.encode()).hexdigest() con_sal = hashlib.sha1((candidata + sal).encode()).hexdigest() sal_ante = hashlib.sha1((sal + candidata).encode()).hexdigest() for nombre, valor in [("sin sal", sin_sal), ("sal detras", con_sal), ("sal delante", sal_ante)]: if valor == objetivo: print("coincide con:", nombre)
Un hallazgo de auditoría no dice «mintieron». Dice qué afirma la fuente, qué muestra la evidencia y qué falta por verificar. Por ejemplo: «La documentación del proveedor declara una sal constante aplicada a todos los registros. Sobre la muestra entregada, los resúmenes se reproducen sin sal. No es posible determinar con esta muestra si la sal se omite en toda la base o solo en estos registros; se requiere revisar el código de registro y de cambio de contraseña.»
Esa última frase —lo que falta por verificar— es la que separa un informe utilizable de una acusación.
Calculen cuánto tarda una función de derivación moderna frente a un resumen simple para el mismo intento, y extrapolen: ¿cuánto costaría revisar las 142.000 cuentas en cada esquema?
El factor de trabajo decide cuánto cuesta cada intento. Un resumen simple está diseñado para ser rápido —es lo que se le pide a un resumen— y por eso es pésimo para guardar contraseñas. Una función de derivación está diseñada para ser lenta a propósito, y esa lentitud es la defensa.
La sal única por usuario decide cuántas veces hay que pagar ese costo. Sin sal, o con una sal constante, el recorrido se hace una sola vez y sirve para las 142.000 cuentas a la vez. Con una sal distinta por usuario, hay que repetir el recorrido entero una vez por cuenta. Ese factor de 142.000 no lo aporta el algoritmo: lo aporta el diseño.
import hashlib, time t = time.perf_counter() hashlib.scrypt(b"candidata", salt=b"sal-unica-16-bytes", n=2**14, r=8, p=1) print(round(time.perf_counter() - t, 3), "segundos por intento")
Seis situaciones. Elijan la causa principal, la que habría evitado el problema.
Una página por equipo al final del bloque. Se evalúa el análisis, no el resultado.