切换到django 3并返回django 2后无法登录Django项目

Posted

技术标签:

【中文标题】切换到django 3并返回django 2后无法登录Django项目【英文标题】:Can't login in Django project after switching to django 3 and return to django 2 【发布时间】:2021-11-20 17:38:06 【问题描述】:

我有一个 Django 2.2 项目,它在一堆不同的服务器上运行,但它们使用相同的数据库。

我创建了一个分支来迁移到 Django 3,但并非所有服务器都会同时迁移。

我使用 Argon2:

# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers  
PASSWORD_HASHERS = [  
 'django.contrib.auth.hashers.Argon2PasswordHasher',  
 'django.contrib.auth.hashers.PBKDF2PasswordHasher',  
 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',  
 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',  
 'django.contrib.auth.hashers.BCryptPasswordHasher',  
]

当我在开发分支中切换到 django 3.2 时,一切正常。但是,当我回到 Django 2.2 时,我开始收到如下错误:

模板语法错误 填充不正确 (exception location: .../python3.6/base64.py in b64decode)

只需删除 cookie 并重新加载即可解决这些问题。所以我猜测它们与 django 3.1 更改为 new default hashing algorithm 从 sha1 到 sha256 有关。

无论如何,重新加载后,页面工作正常。但是当我尝试登录时,它无法识别凭据。

然后我从备份中恢复了数据库并可以登录 django 2.2。

我再次尝试使用以下设置在 django 3.2 上运行:

DEFAULT_HASHING_ALGORITHM = 'sha1'

现在,当切换回 2.2 时,我在页面加载时没有收到错误(我不需要删除 cookie),但凭据仍然不起作用。

对我来说,切换到 django 3.2 后,数据库中密码的哈希值发生了变化。

django 3 是否有可能重写数据库中的密码? 任何人都可以指出解决方案或尝试的东西吗?

谢谢。

【问题讨论】:

【参考方案1】:

解决方案 TL;DR

好吧,如果你使用 Argon2 哈希器,Django 确实会更新存储的密码,如果你想暂时避免它,你必须更新到 Django 3.1,直到准备好移动到 3.2 或子类化哈希器。

比较存储的密码

由于我在 Django 3 中可以访问生产数据库(django 2.2)和本地数据库,因此我比较了我的用户的密码:

Django 2.2(2019 年 4 月发布)

算法:argon2 类型:argon2i 版本:19 内存成本:512 时间成本: 2 并行度: 2 salt: UVn ********** hash: QVt ***** ************

Django 3.2

算法:argon2 品种:argon2id 版本:19 内存成本:102,400 时间成本: 2 并行度: 8 salt: pHQzc2 **************** hash: fj ****************

是的,它们是不同的!

Django 3.2 release notes 讲述默认 Argon2 哈希器的变化:

django.contrib.auth

PBKDF2 密码哈希的默认迭代次数从 216,000 增加到 260,000。

Argon2 密码哈希的默认变体已更改为 Argon2id。 memory_cost 和并行度增加到 102,400 和 8 分别匹配 argon2-cffi 默认值。

增加 memory_cost 会将所需内存从 512 KB 推到 100 MB。这仍然是相当保守的,但可能会导致问题 内存受限的环境。如果是这种情况,现有的 hasher 可以被子类化以覆盖默认值。

Argon2、MD5、PBKDF2、SHA-1 密码哈希器的默认盐熵从 71 位增加到 128 位。

这是改变的票:

#30472 Argon2id should be supported and become the default variety for Argon2PasswordHasher

并且查看django.contrib.auth.hashers中的代码,我可以看到密码在check_password上被修改:

def check_password(password, encoded, setter=None, preferred='default'):
    ...
    hasher_changed = hasher.algorithm != preferred.algorithm
    must_update = hasher_changed or preferred.must_update(encoded)
    is_correct = hasher.verify(password, encoded)
    ...

class Argon2PasswordHasher(BasePasswordHasher):
    ...
    def must_update(self, encoded):
        decoded = self.decode(encoded)
        current_params = decoded['params']
        new_params = self.params()
        ...

    def params(self):
        argon2 = self._load_library()
        # salt_len is a noop, because we provide our own salt.
        return argon2.Parameters(
            type=argon2.low_level.Type.ID,
            version=argon2.low_level.ARGON2_VERSION,
            salt_len=argon2.DEFAULT_RANDOM_SALT_LENGTH,
            hash_len=argon2.DEFAULT_HASH_LENGTH,
            time_cost=self.time_cost,
            memory_cost=self.memory_cost,
            parallelism=self.parallelism,
        )

这一行type=argon2.low_level.Type.ID 将argon2 类型从argon2i 更改为argon2id。其余的变化很明显。

我不确定这是否是真实的过程,但我猜它是这样的:

您输入密码 使用旧算法检查密码 如果匹配,则使用新算法重新散列并保存

(如果我错了,我会很高兴)

回顾一下:我的问题

我的问题是我有几个不同的 Django 2 项目使用相同的公共核心代码和相同的数据库。 尽管它们是不同的项目,但有许多用户可以访问所有这些项目。 我想从最不敏感的地方开始逐步更新它们,看看是否出现错误。

解决方案 1

升级到 Django 3.1 而不是 3.2。这将允许我逐步更新不同的项目,而不会中断用户访问。 一旦所有项目在 3.1 版本中运行了一段时间并修复了出现的任何错误,我就可以更有信心地将它们同时更新到 django 3.2。这是我已经测试过并且有效的方法(它不会更改密码)。

解决方案 2

子类化 django.contrib.auth.hashers.Argon2PasswordHasher 哈希,因此它不会更新密码。在PASSWORD_HASHERS设置中指向它,当所有项目在django 3.2中顺利运行时将其删除。

【讨论】:

以上是关于切换到django 3并返回django 2后无法登录Django项目的主要内容,如果未能解决你的问题,请参考以下文章

Django CSRF攻击

Django使用模板后无法找到静态资源文件

从 1.8 升级到 2.2.4 后,Django 无法创建用于单元测试的表

升级到 2.1 后 Django 管理员无法登录

新鲜的 python 3.7 / django 2.2.1 安装无法识别已安装 mysqlclient

Django自定义视图从1.1更新到2.2后停止工作