【问题标题】:Django upgrading unsalted MD5 password not matchingDjango升级未加盐的MD5密码不匹配
【发布时间】:2020-04-05 21:39:44
【问题描述】:

我正在迁移一个使用未加盐 MD5 密码的旧系统(太可怕了!)。

我知道 Django handles automatically password upgrading,当用户登录时,通过在 settings.pyPASSWORD_HASHERS 列表中添加额外的哈希器。

但是,我想升级密码而不要求用户登录,also explained in the docs

所以,我按照文档中的示例实现了一个自定义哈希器,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 shell check_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

我已经查看了othersimilar SO 问题,但也没有找到合适的解决方案。

【问题讨论】:

  • 你在这里自己生成salt,你不会“存储”在目标密码中。

标签: django passwords


【解决方案1】:

简短回答:不考虑提供的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)

因此,传递给encode(..) 方法的salt 将被忽略。

这意味着如果您以后想要验证密码,Django 将使用它存储的salt 调用encode(..)(在您的情况下,这是编码密码的第二部分, 所以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)

【讨论】:

  • 哦,我完全觉得我的“盐处理”不太正确。感谢您的详细回答。我现在开始工作了。
猜你喜欢
  • 2014-05-03
  • 2010-11-30
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
相关资源
最近更新 更多