django - Django升级未加盐的MD5密码不匹配
问题描述
我正在迁移一个使用未加盐 MD5 密码的旧系统(太可怕了!)。
我知道 Django会在用户登录时自动处理密码升级,方法是PASSWORD_HASHERS
在settings.py
.
但是,我想在不需要用户登录的情况下升级密码,文档中也有解释。
所以,我按照文档中的示例并实现了一个自定义哈希,legacy/hasher.py
:
import secrets
from django.contrib.auth.hashers import PBKDF2PasswordHasher, UnsaltedMD5PasswordHasher
class PBKDF2WrappedMD5PasswordHasher(PBKDF2PasswordHasher):
algorithm = "pbkdf2_wrapped_md5"
def encode_md5_hash(self, md5_hash):
salt = secrets.token_hex(16)
return super().encode(md5_hash, salt)
def encode(self, password, salt, iterations=None):
md5_hash = UnsaltedMD5PasswordHasher().encode(password, salt="")
return self.encode_md5_hash(md5_hash)
并将其添加到settings.py
:
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
"legacy.hashers.PBKDF2WrappedMD5PasswordHasher",
]
但是,在 Django shellcheck_password
中对此进行测试会为升级后的密码返回 False。
>>> from django.contrib.auth.hashers import check_password, UnsaltedMD5PasswordHasher
>>> from legacy.hashers import PBKDF2WrappedMD5PasswordHasher
>>> hasher = PBKDF2WrappedMD5PasswordHasher()
>>> test_pwd = '123456'
>>> test_pwd_unsalted_md5 = UnsaltedMD5PasswordHasher().encode(test_pwd, salt='')
>>> print(test_pwd_unsalted_md5)
'827ccb0eea8a706c4c34a16891f84e7b' # this is an example of a password I want to upgrade
>>> upgraded_test_pwd = hasher.encode_md5_hash(test_pwd)
>>> print(upgraded_test_pwd)
pbkdf2_wrapped_md5$150000$f3aae83b02e8727a2477644eb0aa6560$brqCWW5QuGUoSQ28YNPGUwTLEwZOuMNheN2RxVZGtHQ=
>>> check_password(test_pwd, upgraded_test_pwd)
False
解决方案
简短回答:通过不考虑提供的salt
内容,在验证 Django 时(可能)无法提供相同的编码密码。
发生这种情况的原因是因为您凭空产生了“盐”,而忽略了salt
通过的。事实上,如果我们看一下您的实现,我们会看到:
class PBKDF2WrappedMD5PasswordHasher(PBKDF2PasswordHasher):
algorithm = "pbkdf2_wrapped_md5"
def encode_md5_hash(self, md5_hash):
salt = secrets.token_hex(16) # generating random salt
return super().encode(md5_hash, salt)
def encode(self, password, salt, iterations=None):
md5_hash = UnsaltedMD5PasswordHasher().encode(password, salt='')
return self.encode_md5_hash(md5_hash)
salt
传递给该encode(..)
方法的那个因此被忽略。
这意味着如果您稍后想要验证密码,Django 将调用它存储encode(..)
的密码salt
(在您的情况下,这是编码密码的第二部分,所以f3aae83b02e8727a2477644eb0aa6560
),但您决定将其丢弃,并生成密码使用不同的盐,因此编码的密码不再与您存储在数据库中的密码匹配。
我建议使用盐,例如:
class PBKDF2WrappedMD5PasswordHasher(PBKDF2PasswordHasher):
algorithm = "pbkdf2_wrapped_md5"
def encode_md5_hash(self, md5_hash, salt):
return super().encode(md5_hash, salt)
def encode(self, password, salt, iterations=None):
md5_hash = UnsaltedMD5PasswordHasher().encode(password, salt='')
return self.encode_md5_hash(md5_hash, salt)
推荐阅读
- flutter - Flutter GPU渲染中的帧请求未决含义是什么
- regex - grep -P 查找包含恰好 n 个 A 后跟恰好 n B 的行
- c - 如何将两个变量写入 .txt 文件的同一行?C语言
- python - 由于计算机崩溃重新安装 Django 后,当我尝试访问我的应用程序中的模型时出现以下错误
- javascript - Javascript/HTML 计算器中的按钮仅在擦除结果之前显示计算器结果一秒钟
- php - Laravel问题:如何从@for中的@foreach中排除值
- reactjs - 如何对多个下拉字段进行唯一选择?
- javascript - vue 从服务器渲染的 html 创建组件
- sql - 按周数和月查询销售组
- powerbi - 从列中提取值以用于计算的度量公式