《密码管理:从设备到云端》
第4章 设备端密码管理器搭建(二)
4. 设备端密码管理器搭建(二):主密码哈希验证与密码增删改查
在上一节中,我们完成了密码管理器的基本框架与主密码输入界面。本节将深入核心安全机制——主密码的PBKDF2哈希验证,并实现密码记录的增、删、改、查功能。通过本节学习,你将掌握如何安全地存储验证凭据,并构建一个可用的本地密码管理工具。
4.1 主密码哈希验证(PBKDF2)
为什么需要PBKDF2? 主密码不能以明文形式存储。PBKDF2(基于密码的密钥派生函数2)通过添加盐值(Salt)和多次迭代(Iterations),将用户输入的主密码转换为难以逆向的哈希值,有效抵御暴力破解和彩虹表攻击。
核心流程示意图
PBKDF2 验证代码示例(Python)
import hashlib
import base64
import os
# ---------- 生成哈希(注册/设置主密码时) ----------
def hash_master_password(password: str, salt: bytes = None) -> dict:
"""使用 PBKDF2-SHA256 生成哈希,返回盐值和哈希值"""
if salt is None:
salt = os.urandom(16) # 生成 16 字节随机盐值
iterations = 100000 # 迭代次数
dk = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
iterations
)
return {
'salt': base64.b64encode(salt).decode('utf-8'),
'hash': base64.b64encode(dk).decode('utf-8'),
'iterations': iterations
}
# ---------- 验证主密码(登录时) ----------
def verify_master_password(password: str, stored_salt: str, stored_hash: str, iterations: int) -> bool:
"""验证输入的主密码是否与存储的哈希匹配"""
salt = base64.b64decode(stored_salt)
dk = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
iterations
)
return base64.b64encode(dk).decode('utf-8') == stored_hash
# ---------- 使用示例 ----------
# 设置主密码
result = hash_master_password("MySecureMasterP@ss123")
print(f"盐值: {result['salt']}")
print(f"哈希: {result['hash']}")
# 验证主密码
is_valid = verify_master_password(
"MySecureMasterP@ss123",
result['salt'],
result['hash'],
result['iterations']
)
print(f"验证结果: {is_valid}") # 输出 True
安全提示: 盐值必须随机生成且每个用户唯一;迭代次数建议不低于 100000(2025年标准)。切勿将盐值或迭代次数硬编码在客户端代码中。
4.2 密码记录的增删改查(CRUD)
在通过主密码验证后,用户可以对存储的密码记录进行管理。每条记录通常包含:网站/应用名称、用户名、密码、备注。我们使用加密的本地文件(如 SQLite + AES 加密)来存储这些记录。
数据模型与操作表
CRUD 操作流程图
完整 CRUD 实现示例(Python + SQLite)
import sqlite3
from cryptography.fernet import Fernet
# 初始化加密密钥(实际应用中应从主密码派生)
cipher_key = Fernet.generate_key()
cipher = Fernet(cipher_key)
def encrypt_password(plaintext: str) -> bytes:
return cipher.encrypt(plaintext.encode('utf-8'))
def decrypt_password(ciphertext: bytes) -> str:
return cipher.decrypt(ciphertext).decode('utf-8')
# 连接数据库并创建表
conn = sqlite3.connect('vault.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS vault (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site TEXT NOT NULL,
username TEXT NOT NULL,
password_encrypted BLOB NOT NULL,
note TEXT DEFAULT ''
)
''')
conn.commit()
# ---------- 创建 (Create) ----------
def add_entry(site: str, username: str, password: str, note: str = ""):
enc_pw = encrypt_password(password)
cursor.execute(
"INSERT INTO vault (site, username, password_encrypted, note) VALUES (?, ?, ?, ?)",
(site, username, enc_pw, note)
)
conn.commit()
print(f"✅ 已添加: {site} / {username}")
# ---------- 读取 (Read) ----------
def search_entries(keyword: str = ""):
if keyword:
cursor.execute(
"SELECT id, site, username, password_encrypted, note FROM vault WHERE site LIKE ? OR username LIKE ?",
(f"%{keyword}%", f"%{keyword}%")
)
else:
cursor.execute("SELECT id, site, username, password_encrypted, note FROM vault")
rows = cursor.fetchall()
for row in rows:
decrypted_pw = decrypt_password(row[3])
print(f"ID: {row[0]} | 站点: {row[1]} | 用户: {row[2]} | 密码: {decrypted_pw} | 备注: {row[4]}")
return rows
# ---------- 更新 (Update) ----------
def update_entry(entry_id: int, new_password: str, new_note: str = None):
enc_pw = encrypt_password(new_password)
if new_note is not None:
cursor.execute("UPDATE vault SET password_encrypted=?, note=? WHERE id=?", (enc_pw, new_note, entry_id))
else:
cursor.execute("UPDATE vault SET password_encrypted=? WHERE id=?", (enc_pw, entry_id))
conn.commit()
print(f"✅ 已更新 ID {entry_id}")
# ---------- 删除 (Delete) ----------
def delete_entry(entry_id: int):
cursor.execute("DELETE FROM vault WHERE id=?", (entry_id,))
conn.commit()
print(f"🗑️ 已删除 ID {entry_id}")
# ---------- 使用示例 ----------
add_entry("example.com", "alice", "P@ssw0rd123", "工作邮箱")
add_entry("github.com", "alice_dev", "G!tHub_2025")
search_entries("example")
update_entry(1, "NewP@ss456", "更新备注")
delete_entry(2)
conn.close()
📌 本节要点总结
- PBKDF2 哈希验证:通过盐值 + 高迭代次数保护主密码,验证时比对哈希值而非明文。
- 密码加密存储:每条记录的密码字段使用 AES 等对称加密算法加密,密钥由主密码派生。
- CRUD 操作:使用参数化 SQL 语句防止注入,增删改查均需通过主密码验证后才能执行。
- 安全实践:盐值随机生成、迭代次数不低于 10 万次、加密密钥与数据分离存储。
专注课程资料整理