Introducción
La autenticación multifactor reduce el riesgo de compromiso de cuentas al requerir más de un tipo de prueba. Una contraseña es algo que el usuario conoce. Un segundo factor puede ser un código proveniente de un dispositivo, aplicación, archivo o token de hardware independiente.
En este laboratorio, comenzará con un script de inicio de sesión en Python que solo utiliza contraseña y luego añadirá un segundo factor sencillo. Para mantener el laboratorio accesible para principiantes y determinista, el segundo factor será un código basado en HMAC generado a partir de un archivo secreto protegido.
Probar el inicio de sesión solo con contraseña
En este paso, ejecutará el script de inicio de sesión inicial. Este solo verifica un nombre de usuario y una contraseña, lo cual constituye una autenticación de factor único, ya que la única prueba es algo que el usuario conoce.
Acceda al directorio del proyecto:
cd /home/labex/project
Inspeccione los archivos preparados:
ls -l
Visualice la base de datos de usuarios:
cat users.json
Ejecute un inicio de sesión exitoso solo con contraseña:
python3 login.py alice wonderland
La salida debería ser:
login ok
Ahora intente con una contraseña incorrecta:
python3 login.py alice wrong
Este comando debería imprimir login failed. Un inicio de sesión fallido termina con un estado distinto de cero, lo cual es normal para esta prueba.
Ejecute el verificador para el estado inicial:
python3 mfa-check.py password-only
El resultado esperado es:
password-only: complete
Crear el secreto para el segundo factor
En este paso, creará un secreto independiente para el segundo factor. En un sistema MFA real, este secreto podría residir en una aplicación de autenticación o en un token de hardware. En este laboratorio, se trata de un archivo local para que el comportamiento sea fácil de inspeccionar y verificar.
Cree un valor secreto:
printf 'labex-mfa-secret-2026\n' > mfa_secret.txt
Restrinja el archivo para que solo el propietario pueda leerlo y escribirlo:
chmod 600 mfa_secret.txt
Compruebe los permisos:
ls -l mfa_secret.txt
Los bits de permiso deberían comenzar con -rw-------:
-rw------- ... mfa_secret.txt
Ejecute el verificador para el secreto:
python3 mfa-check.py secret
El resultado esperado es:
secret: complete
Generar un código de inicio de sesión
En este paso, creará un pequeño generador de códigos. Utiliza HMAC-SHA256 para combinar el archivo secreto con el nombre de usuario. HMAC es útil aquí porque la misma entrada y el mismo secreto producen el mismo código corto, pero alguien sin el secreto no puede crear el código fácilmente.
Cree generate-code.py:
cat > generate-code.py <<'PY'
#!/usr/bin/env python3
import hmac
import hashlib
import sys
from pathlib import Path
if len(sys.argv) != 2:
print("usage: generate-code.py USERNAME")
raise SystemExit(2)
username = sys.argv[1]
secret = Path("mfa_secret.txt").read_text().strip().encode()
digest = hmac.new(secret, username.encode(), hashlib.sha256).hexdigest()
print(digest[:6])
PY
Hágalo ejecutable:
chmod +x generate-code.py
Genere un código para alice:
python3 generate-code.py alice
Debería ver este código determinista de seis caracteres:
21f18f
Ejecute el verificador para el generador:
python3 mfa-check.py generator
El resultado esperado es:
generator: complete
Aplicar el segundo factor
En este paso, reemplazará el script de inicio de sesión para que requiera tanto la contraseña como el código generado. Este es el cambio clave de MFA: la contraseña por sí sola ya no es suficiente.
Reemplace login.py con esta versión compatible con MFA:
cat > login.py <<'PY'
#!/usr/bin/env python3
import hmac
import hashlib
import json
import sys
from pathlib import Path
USERS = Path("users.json")
SECRET = Path("mfa_secret.txt")
def expected_code(username):
secret = SECRET.read_text().strip().encode()
digest = hmac.new(secret, username.encode(), hashlib.sha256).hexdigest()
return digest[:6]
def main():
if len(sys.argv) != 4:
print("usage: login.py USERNAME PASSWORD MFA_CODE")
return 2
username, password, code = sys.argv[1], sys.argv[2], sys.argv[3]
users = json.loads(USERS.read_text())
if users.get(username, {}).get("password") != password:
print("login failed")
return 1
if not hmac.compare_digest(code, expected_code(username)):
print("mfa failed")
return 1
print("login ok")
return 0
if __name__ == "__main__":
raise SystemExit(main())
PY
Asegúrese de que el script sea ejecutable:
chmod +x login.py
Genere un código nuevo y guárdelo en una variable de shell:
CODE="$(python3 generate-code.py alice)"
Utilice la contraseña y el código correctos juntos:
python3 login.py alice wonderland "$CODE"
La salida debería ser:
login ok
Ejecute el verificador para la aplicación de MFA:
python3 mfa-check.py enforced
El resultado esperado es:
enforced: complete
Probar el fallo y el éxito de MFA
En este paso, probará el comportamiento final del inicio de sesión. Una contraseña correcta sin el segundo factor debería fallar, un código incorrecto debería fallar, y la contraseña correcta junto con el código correcto deberían tener éxito.
Primero, intente el comando antiguo de solo contraseña:
python3 login.py alice wonderland || true
La salida debería mostrar el nuevo mensaje de uso porque falta el código MFA:
usage: login.py USERNAME PASSWORD MFA_CODE
Intente con un código incorrecto:
python3 login.py alice wonderland 000000 || true
La salida debería ser:
mfa failed
Genere el código correcto e inicie sesión:
CODE="$(python3 generate-code.py alice)"
python3 login.py alice wonderland "$CODE"
La salida debería ser:
login ok
Ejecute el verificador completo:
python3 mfa-check.py all
La salida final debería mostrar que todos los componentes de MFA están completos:
secret: complete
generator: complete
enforced: complete
Resumen
Ha transformado un script de inicio de sesión basado solo en contraseña en un flujo de inicio de sesión multifactor sencillo. Creó un secreto MFA independiente, generó un código basado en HMAC, exigió dicho código en el script de inicio de sesión y probó tanto los casos de fallo como los de éxito.
El concepto de seguridad principal es que una contraseña robada no debería ser suficiente por sí sola cuando se requiere una segunda prueba independiente.



