网站登录凭证加密问题:Fernet失效,求替代加密方案
问题分析与解决方案
核心错误:误解Fernet的使用逻辑
你遇到的问题并非Fernet加密机制本身的问题,而是用法错误:
- Fernet每次加密同一明文时,会生成不同的密文(内置随机初始化向量IV),所以注册时把加密后的用户名存进数据库,登录时再加密输入的用户名去匹配,必然匹配失败——哪怕输入的用户名完全一致,两次加密结果也不同。
- 你用
str(encryptor.encrypt(...))将bytes类型密文转成字符串时,会自动带上b''包裹(比如密文b'xxx'会变成字符串"b'xxx'"),这也会导致存储和查询的字符串格式不一致,进一步加剧匹配失败问题。
更适合网站登录的方案
网站登录场景中,绝对不能用可逆加密存储密码(包括Fernet),一旦密钥泄露,所有用户密码都会被还原。正确做法是使用不可逆的密码哈希算法,常用的有:
- bcrypt:安全性高,适配硬件迭代,优先推荐
- Argon2:密码哈希竞赛冠军,安全性更强
- PBKDF2:Python标准库自带,兼容性好
用户名无需加密,直接明文存储即可;若有隐私需求需加密存储,登录时需取出密文解密后和输入明文对比,而非加密输入去匹配。
修改后的代码示例(基于bcrypt)
先安装bcrypt:
pip install bcrypt
核心代码修改如下:
from flask import Flask, render_template, request,redirect import sqlite3 import bcrypt app = Flask(__name__) @app.route('/', methods=['GET', 'POST']) def login(): if request.method == 'POST': usernameInput = request.form['username'] userpassInput = request.form['password'] db_connect = sqlite3.connect("accounts.db") cur = db_connect.cursor() # 用明文用户名直接查询数据库 cur.execute("""SELECT * FROM accountsDB WHERE userName=? or accessCode=?""", (usernameInput, usernameInput)) userAccountName = cur.fetchone() if userAccountName: # 验证输入密码与数据库中存储的哈希是否匹配 stored_hash = userAccountName[4] if bcrypt.checkpw(userpassInput.encode('utf-8'), stored_hash): return redirect('/userAccountPage', code=302) else: return 'Wrong Password' else: return 'No user Found' return render_template('login.html') @app.route("/registration", methods=['GET', 'POST']) def registrationPage(): if request.method == 'POST': username_reg = request.form['username'] userCode_reg = request.form['aime-access-code'] userMaiName_reg = request.form['maimaiIGN'] userpassword_reg = request.form['initialPassword'] # 对密码生成哈希,仅存储哈希值 pass_hash = bcrypt.hashpw(userpassword_reg.encode('utf-8'), bcrypt.gensalt()) db_connect = sqlite3.connect("accounts.db") cur = db_connect.cursor() # 用户名等信息明文存储,密码存储哈希值 cur.execute("""INSERT INTO accountsDB(userName, accessCode, maimaiIGN, userPassword) VALUES (?,?,?,?)""", (username_reg, userCode_reg, userMaiName_reg, pass_hash)) db_connect.commit() return redirect('/', code=302) return render_template('registration.html')
额外注意事项
- 数据库中
userPassword字段需设置为足够长的类型(如TEXT),因为bcrypt生成的哈希字符串长度固定且较长。 - 若需加密敏感信息(如accessCode),可继续使用Fernet,但查询时要取出密文解密后和输入明文对比;同时密钥需妥善保管,避免硬编码在代码中,建议通过环境变量加载。
内容的提问来源于stack exchange,提问作者Lord_Kordec
相关产品推荐
相关产品推荐

