为登录脚本添加双重验证

网络安全Beginner
立即练习

简介

多重身份验证(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 的验证码,在登录脚本中强制要求该验证码,并测试了失败和成功的情况。

其核心安全理念是:当需要独立的第二个证明时,仅凭被窃取的密码已不足以完成登录。