简介
多重身份验证(Multi-factor authentication)要求提供不止一种证明方式,从而降低账户被入侵的风险。密码是用户「所知」的信息。第二个验证因子可以是来自独立设备、应用程序、文件或硬件令牌的验证码。
在本实验中,你将从一个仅使用密码的 Python 登录脚本开始,然后为其添加一个简单的第二个验证因子。为了使实验对初学者友好且结果确定,第二个验证因子将是一个基于 HMAC 生成的验证码,该验证码源自一个受保护的密钥文件。
测试仅密码登录
在此步骤中,你将运行初始的登录脚本。它仅检查用户名和密码,这属于单因素身份验证,因为唯一的证明方式就是用户「所知」的信息。
进入项目目录:
cd /home/labex/project
查看准备好的文件:
ls -l
查看用户数据库:
cat users.json
运行一次成功的仅密码登录:
python3 login.py alice wonderland
输出应为:
login ok
现在尝试输入错误的密码:
python3 login.py alice wrong
该命令应打印 login failed。登录失败会以非零状态码退出,这对于此测试是正常的。
运行初始状态的检查器:
python3 mfa-check.py password-only
预期结果为:
password-only: complete
创建第二个验证因子的密钥
在此步骤中,你将为第二个验证因子创建一个独立的密钥。在真实的多重身份验证(MFA)系统中,该密钥可能存储在身份验证器应用或硬件令牌中。在本实验中,它是一个本地文件,以便于检查和验证其行为。
创建一个密钥值:
printf 'labex-mfa-secret-2026\n' > mfa_secret.txt
限制文件权限,使其仅所有者可读写:
chmod 600 mfa_secret.txt
检查权限:
ls -l mfa_secret.txt
权限位应以 -rw------- 开头:
-rw------- ... mfa_secret.txt
运行密钥检查器:
python3 mfa-check.py secret
预期结果为:
secret: complete
生成登录验证码
在此步骤中,你将创建一个小型验证码生成器。它使用 HMAC-SHA256 将密钥文件与用户名结合起来。HMAC 在此非常有用,因为相同的输入和密钥会产生相同的短验证码,但没有密钥的人无法轻易伪造该验证码。
创建 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
使其可执行:
chmod +x generate-code.py
为 alice 生成验证码:
python3 generate-code.py alice
你应该会看到这个确定的六字符验证码:
21f18f
运行生成器检查器:
python3 mfa-check.py generator
预期结果为:
generator: complete
强制执行第二个验证因子
在此步骤中,你将替换登录脚本,使其同时要求密码和生成的验证码。这是 MFA 的关键改动:仅有密码已不再足够。
将 login.py 替换为支持 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
确保脚本可执行:
chmod +x login.py
生成一个新的验证码并将其保存到 shell 变量中:
CODE="$(python3 generate-code.py alice)"
同时使用正确的密码和验证码进行登录:
python3 login.py alice wonderland "$CODE"
输出应为:
login ok
运行 MFA 强制执行检查器:
python3 mfa-check.py enforced
预期结果为:
enforced: complete
测试 MFA 的失败与成功
在此步骤中,你将测试最终的登录行为。正确的密码但缺少第二个验证因子应失败,错误的验证码应失败,而正确的密码加上正确的验证码则应成功。
首先,尝试旧的仅密码命令:
python3 login.py alice wonderland || true
输出应显示新的用法提示,因为缺少 MFA 验证码:
usage: login.py USERNAME PASSWORD MFA_CODE
尝试错误的验证码:
python3 login.py alice wonderland 000000 || true
输出应为:
mfa failed
生成正确的验证码并登录:
CODE="$(python3 generate-code.py alice)"
python3 login.py alice wonderland "$CODE"
输出应为:
login ok
运行完整检查器:
python3 mfa-check.py all
最终输出应显示所有 MFA 组件均已完成:
secret: complete
generator: complete
enforced: complete
总结
你已将一个仅密码的登录脚本更改为简单的多重身份验证登录流程。你创建了一个独立的 MFA 密钥,生成了基于 HMAC 的验证码,在登录脚本中强制要求该验证码,并测试了失败和成功的情况。
其核心安全理念是:当需要独立的第二个证明时,仅凭被窃取的密码已不足以完成登录。



