Django 密码在数据库中的存储格式是什么?

35 投票
3 回答
28378 浏览
提问于 2025-04-15 11:07

你知道Django是怎么存储密码的吗?它的存储方式是这样的:

sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4

这里面有个格式是“哈希类型 $盐 $哈希”。我想问的是,$哈希是怎么来的?是把密码和盐混合在一起然后再进行哈希处理,还是说有其他的方式呢?

3 个回答

13

很长一段时间,直到1.3版本,Django确实采用了一种不负责任的做法,就是用简单的SHA1算法和一个太短的盐值来存储密码信息。这种方法自1979年以来就已经过时了。用这种方式存储的密码非常容易受到暴力破解攻击。想了解更多原因,可以查看关于密码哈希的安全讨论

自2012年1.4版本以来,Django默认使用一种基于标准密钥派生函数PBKDF2的哈希算法,这种算法的迭代次数是可以配置的,默认值在每个版本中都会增加(在1.7版本中是20000次)。它还支持bcrypt,并且与早期版本兼容,用户登录时会自动升级密码哈希。想了解更多,可以查看Django中的密码管理 | Django文档

22

根据文档

哈希类型可以是 sha1(默认)、md5 或 crypt,这些是用来对密码进行单向加密的算法。盐是一个随机字符串,用来给原始密码加盐,以生成哈希。

根据set_password的代码:

def set_password(self, raw_password):
    import random
    algo = 'sha1'
    salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
    hsh = get_hexdigest(algo, salt, raw_password)
    self.password = '%s$%s$%s' % (algo, salt, hsh)

文档中提到,哈希是由盐、算法和密码组合后进行加密的结果。

46

和往常一样,记得查看源代码:

# root/django/trunk/django/contrib/auth/models.py
# snip
def get_hexdigest(algorithm, salt, raw_password):
    """
    Returns a string of the hexdigest of the given plaintext password and salt
    using the given algorithm ('md5', 'sha1' or 'crypt').
    """
    raw_password, salt = smart_str(raw_password), smart_str(salt)
    if algorithm == 'crypt':
        try:
            import crypt
        except ImportError:
            raise ValueError('"crypt" password algorithm not supported in this environment')
        return crypt.crypt(raw_password, salt)

    if algorithm == 'md5':
        return md5_constructor(salt + raw_password).hexdigest()
    elif algorithm == 'sha1':
        return sha_constructor(salt + raw_password).hexdigest()
    raise ValueError("Got unknown password algorithm type in password.")

我们可以看到,密码的摘要是通过把“盐”(salt)和密码连接在一起,然后用选定的哈希算法处理得到的。接着,算法的名称、原始的盐和密码的哈希值用“$”符号分开连接,形成最终的摘要。

# Also from root/django/trunk/django/contrib/auth/models.py
def check_password(raw_password, enc_password):
    """
    Returns a boolean of whether the raw_password was correct. Handles
    encryption formats behind the scenes.
    """
    algo, salt, hsh = enc_password.split('$')
    return hsh == get_hexdigest(algo, salt, raw_password)

为了验证密码,Django只需检查相同的盐和相同的密码是否能得到相同的摘要。

撰写回答