解决方案 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盐:UVn **********哈希:QVt ************** ***
Django 3.2
算法:argon2品种:argon2id版本:19内存成本:102,400时间成本:2并行度:8盐:pHQzc2 ****************哈希:flj ******** ********
是的,它们是不同的!
Django 3.2 发行说明讲述了默认 Argon2 哈希器的变化:
django.contrib.auth
PBKDF2 密码哈希的默认迭代次数从 216,000 增加到 260,000。
Argon2 密码哈希的默认变体更改为 Argon2id。memory_cost 和并行度分别增加到 102,400 和 8 以匹配 argon2-cffi 默认值。
增加 memory_cost 会将所需内存从 512 KB 推到 100 MB。这仍然相当保守,但会在内存受限的环境中导致问题。如果是这种情况,现有的哈希可以被子类化以覆盖默认值。
Argon2、MD5、PBKDF2、SHA-1 密码哈希器的默认盐熵从 71 位增加到 128 位。
这是改变的门票:
#30472 应该支持 Argon2id 并成为 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 中顺利运行时将其删除。