我正在迁移使用未加盐的MD5密码(恐怖!)的旧系统。
在用户登录时,我通过在PASSWORD_HASHERS
的{{1}}列表中添加其他哈希来了解Django handles automatically password upgrading。
但是,我想升级密码而不要求用户登录also explained in the docs。
因此,我遵循了文档中的示例并实现了自定义哈希器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
但是,在Django shell PASSWORD_HASHERS = [
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
"legacy.hashers.PBKDF2WrappedMD5PasswordHasher",
]
中对其进行测试会为升级后的密码返回False。
check_password
答案 0 :(得分:2)
简短答案:在验证Django无法(可能)提供相同的编码密码时,不考虑提供的salt
。
发生这种情况的原因是因为您凭空产生“盐”,而忽略了所传递的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
),但是您决定扔掉它,并生成带有不同盐的密码,因此,编码密码不再与您存储在数据库中的密码匹配。 / p>
我建议使用盐,例如:
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)