【问题标题】:How to check password against previously used passwords in django如何根据 django 中以前使用的密码检查密码
【发布时间】:2020-12-11 19:29:05
【问题描述】:

我有以下模型用于存储以前使用的散列密码:

class PasswordHistory(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    password = models.CharField(max_length=128, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now = True)

在更改密码表单中,我想检查用户更改的新密码是否在过去 5 次未使用过。

这是我的表单验证:

class ProfileForm(forms.ModelForm):
    password1 = forms.CharField(widget=forms.PasswordInput(), required=False)
    password2 = forms.CharField(widget=forms.PasswordInput(), required=False)

    class Meta:
        model = Employee

        
    user_id = None
    
    def __init__(self, *args, **kwargs):
        self.user_id = kwargs.pop('user_id', None)
        super(ProfileForm, self).__init__(*args, **kwargs)
        
    def clean_password2(self):
        password1 = self.cleaned_data['password1']
        password2 = self.cleaned_data['password2']
        if password1 != password2:
            raise forms.ValidationError('Passwords do not match.')
        

        user = User.objects.get(pk=self.user_id)
        hashed_password = make_password(password1)
        password_histories = PasswordHistory.objects.filter(
        user=user,
        password_hashed_password
    )
    if password_histories.exists():
        raise forms.ValidationError('That password has already been used')        
    return password2

问题在于密码每次都不同,即使我一遍又一遍地尝试相同的纯文本密码。因此:

if password_histories.exists():

从不返回 true。

如果过去的密码因加盐而总是不同,我该如何比较它们? 谢谢

【问题讨论】:

  • user.set_password返回密码,它只是设置密码。

标签: django django-forms django-authentication django-users


【解决方案1】:

.set_password 函数确实不返回任何内容,它只是设置密码。但是,就像您说的那样,散列基于(随机)盐,因此散列每次都会有所不同。因此,您应该使用.check_password(…) function [Django-doc] 来验证它是否以某种方式与散列变体匹配:

from django.contrib.auth.hashers import check_password

class ProfileForm(forms.ModelForm):
    password1 = forms.CharField(widget=forms.PasswordInput(), required=False)
    password2 = forms.CharField(widget=forms.PasswordInput(), required=False)

    class Meta:
        model = Employee
    
    def __init__(self, *args, **kwargs):
        self.user_id = kwargs.pop('user_id', None)
        super(ProfileForm, self).__init__(*args, **kwargs)
        
    def clean_password2(self):
        password1 = self.cleaned_data['password1']
        password2 = self.cleaned_data['password2']
        if password1 != password2:
            raise forms.ValidationError('Passwords do not match.')
        user = User.objects.get(pk=self.user_id)
        password_histories = PasswordHistory.objects.filter(
            user=user
        )
        for pw in password_histories:
            if check_password(password2, pw.password):
                raise forms.ValidationError('That password has already been used')
        return password2

因此,如果我们找到与给定原始密码匹配的哈希密码,我们可以返回password。如果在for循环结束时,我们没有找到任何这样的密码,我们可以返回password2,否则我们会报错。

【讨论】:

  • make_password 为相同的纯文本密码打印出完全不同的哈希值。
  • @Atma:这很奇怪,因为User.set_password 在幕后调用:github.com/django/django/blob/master/django/contrib/auth/… 所以除非你改变了哈希器等,否则会发生一些可疑的事情。请注意,您应该小心不要双重哈希密码(就像已经对哈希版本进行哈希一样)。这就是为什么在清洁期间致电.set_password 不是一个好主意。从那时起,您将经常在save 方法或视图中再次调用它,从而存储散列散列
  • 即使在 django 管理员中也是如此。我可以一遍又一遍地将密码设置为相同的纯文本密码,并且每次都会因为盐而不同。
  • @Atma:啊,是的,你确实应该验证哈希,而不是哈希密码。
  • 帮助别人的人,这是强大的东西
猜你喜欢
  • 1970-01-01
  • 2017-11-16
  • 2021-04-01
  • 2011-03-20
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 2020-09-05
  • 1970-01-01
相关资源
最近更新 更多