MFA
MFA(Multi-Factor Authentication,多因素认证)是一种安全系统,它要求用户提供两种或多种独立的认证因素来验证其身份。通过结合不同类型的认证因素,MFA显著提高了系统的安全性,降低了未经授权访问的风险。
多因素认证的三个主要因素
知识因素(你知道的):
典型示例:密码、PIN码、安全问题的答案。
所有因素(你拥有的):
典型示例:智能卡、手机、硬件令牌、软件令牌(如Google Authenticator)。
生物因素(你是什么):
典型示例:指纹、面部识别、虹膜扫描、语音识别。
常见的MFA实现方式
SMS验证码:
用户在登录时,系统会向用户注册的手机号码发送一个一次性验证码,用户需要输入该验证码完成认证。
时间同步令牌(TOTP):
用户使用手机上的应用程序(如Google Authenticator)生成一个一次性密码。该密码基于当前时间和共享的密钥生成,每30秒更新一次。
硬件令牌:
用户持有一个物理设备(如YubiKey),设备可以生成一次性密码或通过USB、NFC接口与计算机进行交互。
生物识别:
用户通过生物特征(如指纹、面部识别)进行认证。
推送通知:
用户在尝试登录时,会收到一条推送通知,通过点击通知中的确认按钮完成认证。
虚拟MFA
虚拟Multi-Factor Authentication (MFA) 是能产生6位数字认证码的设备或应用程序,遵循基于时间的一次性密码 (TOTP)标准。MFA设备可以基于硬件也可以基于软件,目前仅支持基于软件的虚拟MFA,即虚拟MFA应用程序,可以在移动硬件设备(包括智能手机)上运行,非常方便,虚拟MFA是多因素认证方式中的一种。
常用的虚拟MFA应用程序,Google Authenticator或Microsoft Authenticator
什么是TOTP,根据百度百科
TOTP算法(Time-based One-time Password algorithm)是一种从共享密钥和当前时间计算一次性密码的算法。 它已被采纳为Internet工程任务组标准RFC 6238,是Initiative for Open Authentication(OATH)的基石,并被用于许多双因素身份验证系统。
TOTP是基于散列的消息认证码(HMAC)的示例。 它使用加密哈希函数将密钥与当前时间戳组合在一起以生成一次性密码。 由于网络延迟和不同步时钟可能导致密码接收者必须尝试一系列可能的时间来进行身份验证,因此时间戳通常以30秒的间隔增加,从而减少了潜在的搜索空间。
下面是用flask实现的简单的虚拟mfa登录验证流程
from flask import Flask, request, jsonify
import pyotp
app = Flask(__name__)
# 生成一个基于 Base32 的随机密钥(用户的密钥)
# 此秘钥需要保存于数据库之中,用于生成totp密码,同样需要发送给用户。在相应的虚拟mfa认# 证软件中生成totp密码
def generate_secret():
return pyotp.random_base32()
# 创建一个TOTP对象
def create_totp(secret):
return pyotp.TOTP(secret)
# 验证TOTP密码,比较用户输入和系统生成的totp密码
def verify_totp(totp, otp):
return totp.verify(otp)
# 用户名与秘钥映射列表,实际业务中应存在数据库中
users = {
"user1": generate_secret(),
"user2": generate_secret()
}
# 用户登录接口
@app.route("/login", methods=["POST"])
def login():
data = request.json
username = data["username"]
password = data["password"]
# 用户端根据秘钥生成的totp密码
otp = data["otp"]
# 验证用户名和密码
if username in users and password == "password":
# 获取用户名对应的秘钥,用于生成totp密码
user_secret = users[username]
totp = create_totp(user_secret)
# 验证OTP
if verify_totp(totp, otp):
return jsonify({"message": "Login successful"})
else:
return jsonify({"error": "Invalid OTP"}), 401
else:
return jsonify({"error": "Invalid username or password"}), 401
if __name__ == "__main__":
app.run(debug=True)