DJANGO 中的 Argon2 和会话变量中的密码

问题描述

到目前为止,我使用 SHA256 来散列密码(我知道,很糟糕),现在我想切换到 Argon2。

我的问题是,如果我理解正确,我现在必须将纯文本密码存储在会话变量中。

现在我将 SHA256 哈希值存储在会话变量中,并且我有一个 check_user 函数来检查用户是否仍然登录使用我的大多数函数

def check_user(request):
    if 'email' not in request.session or not request.session['email']:
        return False
    if 'pw_hash' not in request.session or not request.session['pw_hash']:
        return False
    try:
        Clients.objects.get(pk=request.session['email'])
    except Clients.DoesNotExist:
        return False
    if request.session['pw_hash'] != Clients.objects.get(pk=request.session['email']).password:
        return False
    return True

如果我切换到 Argon2,据我所知,我必须将普通密码存储在会话变量中才能使用 PasswordHasher().verify,这在 IMO 是一种不好的做法。

解决这个问题的推荐方法是什么?

解决方法

由于对默认身份验证系统缺乏了解,您已经推出了自己的系统并使用不同的 API 实现了几乎相同的内容。

Django 在使用 login 函数时做了什么(为简洁起见进行了简化):

  • 在会话中存储用户 ID
  • 在会话中存储用于身份验证的后端
  • 使用盐存储哈希密码的 SHA-256 哈希值(以确保源字符串至少为 256/8 = 32 字节长)

最后一点类似于您的 check_user 方法,只是它更安全一点:它不会将密码哈希公开给会话存储,因此真正的密码哈希只在一个地方。

然后中间件调用 get_user 来验证这个哈希值。

它比较使用更快的方法(sha-256)只是为了排除会话属于另一个用户或密码已在不同浏览器中更改:如果您在桌面上更改密码,您的手机手机将在下次尝试使用其会话时注销(前提是它们不共享 cookie)。

这个更简单的比较并不会降低它的安全性,因为为了猜测密码,你仍然需要破解真正的哈希值(在你的情况下是 Argon-2),而你甚至没有那个哈希值如果您获得了会话存储的访问权限。

总而言之,我强烈建议彻底阅读 Django 身份验证系统以及如何扩展它,因为它经过了比您自己的系统更多的用户的审查和实战测试。此外,Django 生态系统中更多使用 Django 用户的包将向您开放。

同时,您可以使用与上图相同的机制:

from django.utils.crypt import constant_time_compare

def check_user(request):
    if 'email' not in request.session or not request.session['email']:
        return False
    if 'pw_hash' not in request.session or not request.session['pw_hash']:
        return False
    try:
        user = Clients.objects.get(pk=request.session['email'])
    except Clients.DoesNotExist:
        return False

    return constant_time_compare(session['pw_hash'],user.password)