Python3.5+Django+Bcrypt迁移后密码验证出现无效盐问题求助
解决Python 3.5中bcrypt密码对比「无效盐」的问题
这个问题我之前帮人排查过,核心原因是Python 2和3对字符串、字节的处理逻辑差异,再加上bcrypt库的类型要求导致的,咱们一步步理清楚:
问题根源
在Python 2.7里,bcrypt.hashpw()返回的是字节串类型的str,直接存入数据库后,到了Python 3.5中,数据库读取出来的内容会变成Unicode字符串类型的str。而Python 3版本的bcrypt库要求:用来验证的盐(也就是数据库里存的密码哈希)必须是字节串,如果传入Unicode字符串,就会触发「无效盐」的错误。
另外还要确认你用的bcrypt版本,Python 3需要安装支持py3的版本(比如bcrypt>=3.1.0),旧版本可能存在兼容性问题。
解决方案
根据你的代码情况,有两种靠谱的处理方式:
方式一:修改验证代码,将读取的哈希转为字节串
假设你原来的验证代码大概是这样(错误的写法):
# Python 3中错误的验证代码 stored_pwd = Password.objects.get(user=current_user).pwd if bcrypt.hashpw(request.POST['password'].encode(), stored_pwd) == stored_pwd: # 登录逻辑
改成下面的写法,先把数据库读取的哈希转成字节串:
# Python 3中正确的验证代码 stored_pwd = Password.objects.get(user=current_user).pwd # 将Unicode字符串转为字节串 stored_pwd_bytes = stored_pwd.encode('utf-8') # 验证时传入字节串类型的盐 if bcrypt.hashpw(request.POST['password'].encode('utf-8'), stored_pwd_bytes) == stored_pwd_bytes: # 登录逻辑
方式二:修改存储代码,统一存字符串格式的哈希
如果你希望从根源上统一存储格式,可以在Python 3中存储时就把字节串转成Unicode字符串,这样后续验证也更清晰:
# Python 3中修改后的存储代码 # 先对密码编码,生成哈希后转成字符串 password_hash = bcrypt.hashpw(request.POST['password'].encode('utf-8'), bcrypt.gensalt()).decode('utf-8') new_user = User.objects.create(first_name=first_name, last_name=last_name, email=email, birthday=birthday) Password.objects.create(pwd=password_hash, user=new_user)
验证时同样转成字节串即可:
stored_pwd = Password.objects.get(user=current_user).pwd stored_pwd_bytes = stored_pwd.encode('utf-8') if bcrypt.hashpw(request.POST['password'].encode('utf-8'), stored_pwd_bytes) == stored_pwd_bytes: # 登录逻辑
额外建议
如果你的数据库字段用的是BinaryField(二进制类型),那在Python 3中读取出来的就是字节串,直接用就行,不用转码。如果是CharField,就按照上面的方式处理。
内容的提问来源于stack exchange,提问作者Duane
相关产品推荐
相关产品推荐

