Python终端密码管理器主密码验证失效问题求助
解决密码管理器主密码验证失效问题
问题根源
你当前的代码存在两个核心问题:
- 直接将存储的密钥与主密码拼接生成Fernet密钥,不符合Fernet对密钥的格式要求(必须是32字节的URL-safe base64编码),且无法验证主密码是否正确。
- 没有任何验证主密码的步骤,导致错误密码也能初始化Fernet对象,程序看似正常运行,直到解密时才会出错(但用户可能误以为程序正常)。
解决方案
我们需要使用**密钥派生函数(PBKDF2HMAC)**从主密码生成合法的Fernet密钥,同时添加主密码验证机制:
- 首次启动时,生成随机盐,用主密码+盐派生密钥,同时存储盐和一个加密的验证字符串(用于后续验证密码)。
- 后续启动时,加载盐和验证字符串,用输入的密码派生密钥,尝试解密验证字符串,成功则密码正确,否则退出程序。
修改后的完整代码
from cryptography.fernet import Fernet from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend import os import base64 def derive_key(master_pwd, salt): # 使用PBKDF2HMAC从主密码和盐派生符合要求的Fernet密钥 kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend() ) return base64.urlsafe_b64encode(kdf.derive(master_pwd.encode())) def setup_master_password(): # 首次设置主密码流程 master_pwd = input("设置你的主密码: ") confirm_pwd = input("确认主密码: ") if master_pwd != confirm_pwd: print("两次密码不一致,设置失败") exit() # 生成随机盐 salt = os.urandom(16) # 派生密钥 key = derive_key(master_pwd, salt) fer = Fernet(key) # 加密固定验证字符串,用于后续验证密码正确性 verification_str = fer.encrypt(b"VALID_MASTER_PASSWORD") # 存储盐和验证字符串 with open("salt.key", "wb") as f: f.write(salt) with open("verification.key", "wb") as f: f.write(verification_str) print("主密码设置成功,请重新启动程序") exit() def load_and_verify_master_password(): # 加载验证信息并验证主密码 try: with open("salt.key", "rb") as f: salt = f.read() with open("verification.key", "rb") as f: verification_str = f.read() except FileNotFoundError: # 无验证文件,触发首次设置流程 setup_master_password() master_pwd = input("输入你的主密码: ") key = derive_key(master_pwd, salt) fer = Fernet(key) # 尝试解密验证字符串,验证密码正确性 try: decrypted = fer.decrypt(verification_str) if decrypted != b"VALID_MASTER_PASSWORD": raise Exception("验证失败") except: print("主密码错误,程序退出") exit() return fer def add(fer): name = input("输入网站名称: ") url = input("输入网站URL: ") email = input("输入邮箱: ") pwd = input("输入密码: ") with open('passwords.txt', 'a') as f: f.write(f"Name: {name} | URL: {url} | Email: {email} | {fer.encrypt(pwd.encode()).decode()}\n") def view(fer): try: with open('passwords.txt', 'r') as f: for line in f.readlines(): data = line.rstrip() if not data: continue name, url, email, pwd = data.split("|") print(f"{name.strip()} | {url.strip()} | {email.strip()} | 密码: {fer.decrypt(pwd.strip().encode()).decode()}") except FileNotFoundError: print("暂无存储的密码") except: print("密码解密失败,可能是主密码错误或数据损坏") # 初始化:验证主密码并获取Fernet对象 fer = load_and_verify_master_password() while True: print("\n1. 添加新密码") print("2. 查看已有密码") print("输入 q 退出") mode = input("选择操作: ") if mode == "q": print("再见 :)") break elif mode == "1": add(fer) elif mode == "2": view(fer) else: print("无效输入,请重新选择")
关键修改说明
- 规范密钥生成:使用
PBKDF2HMAC从主密码和随机盐生成符合Fernet要求的32字节密钥,避免直接拼接的错误逻辑。 - 强制密码验证:首次设置时加密固定字符串,后续启动时必须解密成功才能继续操作,确保只有正确的主密码能进入程序。
- 完善流程处理:自动检测首次运行场景,触发主密码设置流程;添加文件不存在、解密失败等场景的错误提示。
内容的提问来源于stack exchange,提问作者Nathan James
相关产品推荐
相关产品推荐

