You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python终端密码管理器主密码验证失效问题求助

解决密码管理器主密码验证失效问题

问题根源

你当前的代码存在两个核心问题:

  • 直接将存储的密钥与主密码拼接生成Fernet密钥,不符合Fernet对密钥的格式要求(必须是32字节的URL-safe base64编码),且无法验证主密码是否正确。
  • 没有任何验证主密码的步骤,导致错误密码也能初始化Fernet对象,程序看似正常运行,直到解密时才会出错(但用户可能误以为程序正常)。

解决方案

我们需要使用**密钥派生函数(PBKDF2HMAC)**从主密码生成合法的Fernet密钥,同时添加主密码验证机制:

  1. 首次启动时,生成随机盐,用主密码+盐派生密钥,同时存储盐和一个加密的验证字符串(用于后续验证密码)。
  2. 后续启动时,加载盐和验证字符串,用输入的密码派生密钥,尝试解密验证字符串,成功则密码正确,否则退出程序。

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 00:50:22