Django Can';t使用自定义密码\u哈希器进行身份验证
我正在使用php将一个网站迁移到Django框架 有一个特定的哈希密码算法,所以我不得不写:Django Can';t使用自定义密码\u哈希器进行身份验证,django,django-authentication,Django,Django Authentication,我正在使用php将一个网站迁移到Django框架 有一个特定的哈希密码算法,所以我不得不写: #settings.py PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'project.hashers.SHA1ProjPasswordHasher', # that's mine 'django.contrib.auth.hashers.PBKDF2SHA1Pas
#settings.py
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'project.hashers.SHA1ProjPasswordHasher', # that's mine
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
...
)
以及:
当PBKDF2PasswordHasher
是第一个时,它工作得很好:
>>> from django.contrib.auth import authenticate
>>> u = authenticate(username='root', password='test')
>>> u.password
u'pbkdf2_sha256$10000$EX8BcgPFjygx$HvB6NmZ7uX1rWOOPbHRKd8GLYD3cAsQtlprXUq1KGMk='
>>> exit()
然后我把我的SHA1ProjPasswordHasher
放在第一位,第一次身份验证非常有效。哈希已更改:
>>> from django.contrib.auth import authenticate
>>> u = authenticate(username='root', password='test')
>>> u.password
'a94a8fe5'
>>> exit()
第二次身份验证失败。无法使用新哈希进行身份验证
>>> from django.contrib.auth import authenticate
>>> u = authenticate(username='root', password='test')
>>> u.password
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'password'
一切正常。我不明白为什么..只有未加盐的md5哈希不能包含美元符号:
# django/contrib/auth/hashers.py
def identify_hasher(encoded):
"""
Returns an instance of a loaded password hasher.
Identifies hasher algorithm by examining encoded hash, and calls
get_hasher() to return hasher. Raises ValueError if
algorithm cannot be identified, or if hasher is not loaded.
"""
if len(encoded) == 32 and '$' not in encoded:
algorithm = 'unsalted_md5'
else:
algorithm = encoded.split('$', 1)[0]
return get_hasher(algorithm)
因此,最好的方法是将当前密码哈希转换为以下格式:alg$salt$hash
class SHA1ProjPasswordHasher(BasePasswordHasher):
"""
Special snowflake algorithm from the first version.
php code: $pass=substr(sha1(trim($_POST['password'])),0,8);
"""
algorithm = "unsalted_and_trimmed_sha1"
def salt(self):
return ''
def encode(self, password, salt):
assert password
assert '$' not in salt
hash = hashlib.sha1(force_bytes(salt + password)).hexdigest()[:8]
return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
encoded_2 = self.encode(password, salt)
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
return SortedDict([
(_('algorithm'), algorithm),
(_('salt'), mask_hash(salt, show=2)),
(_('hash'), mask_hash(hash)),
])
>来自django.contrib.auth导入验证
>>>x=验证(用户名='root',密码='test')
>>>x
>>>密码
u'unsalted_和_trimmed_sha1$$a94a8fe5'
只有未加盐的md5哈希不能包含美元符号:
# django/contrib/auth/hashers.py
def identify_hasher(encoded):
"""
Returns an instance of a loaded password hasher.
Identifies hasher algorithm by examining encoded hash, and calls
get_hasher() to return hasher. Raises ValueError if
algorithm cannot be identified, or if hasher is not loaded.
"""
if len(encoded) == 32 and '$' not in encoded:
algorithm = 'unsalted_md5'
else:
algorithm = encoded.split('$', 1)[0]
return get_hasher(algorithm)
因此,最好的方法是将当前密码哈希转换为以下格式:alg$salt$hash
class SHA1ProjPasswordHasher(BasePasswordHasher):
"""
Special snowflake algorithm from the first version.
php code: $pass=substr(sha1(trim($_POST['password'])),0,8);
"""
algorithm = "unsalted_and_trimmed_sha1"
def salt(self):
return ''
def encode(self, password, salt):
assert password
assert '$' not in salt
hash = hashlib.sha1(force_bytes(salt + password)).hexdigest()[:8]
return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
encoded_2 = self.encode(password, salt)
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
return SortedDict([
(_('algorithm'), algorithm),
(_('salt'), mask_hash(salt, show=2)),
(_('hash'), mask_hash(hash)),
])
>来自django.contrib.auth导入验证
>>>x=验证(用户名='root',密码='test')
>>>x
>>>密码
u'unsalted_和_trimmed_sha1$$a94a8fe5'
虽然几年过去了,但我还是把我的解决方案放在这里以备将来参考
弗拉德部分正确;django.contrib.auth.hasher中的以下方法似乎迫使您使用包含美元符号的哈希格式来标记django用于决定使用哪个哈希程序的算法
def identify_hasher(encoded):
"""
Returns an instance of a loaded password hasher.
Identifies hasher algorithm by examining encoded hash, and calls
get_hasher() to return hasher. Raises ValueError if
algorithm cannot be identified, or if hasher is not loaded.
"""
# Ancient versions of Django created plain MD5 passwords and accepted
# MD5 passwords with an empty salt.
if ((len(encoded) == 32 and '$' not in encoded) or
(len(encoded) == 37 and encoded.startswith('md5$$'))):
algorithm = 'unsalted_md5'
# Ancient versions of Django accepted SHA1 passwords with an empty salt.
elif len(encoded) == 46 and encoded.startswith('sha1$$'):
algorithm = 'unsalted_sha1'
else:
algorithm = encoded.split('$', 1)[0]
return get_hasher(algorithm)
不过,有一种方法可以“欺骗”django,而不必对django安装进行黑客攻击。您必须创建用于身份验证的身份验证后端。在这里,您将覆盖django的check_password方法。我有一个数据库,其中散列是{SSHA512}hash
,我无法更改它,因为我必须能够与dovecot通信。因此,我在我的backends.py
类中添加了以下内容:
def check_password(self, raw_password, user):
"""
Returns a boolean of whether the raw_password was correct. Handles
hashing formats behind the scenes.
"""
def setter(raw_password):
user.set_password(raw_password)
user.save(update_fields=["password"])
return check_password(raw_password, "SSHA512$" + user.password, setter)
这样,当django必须检查密码是否正确时,它将执行以下操作:
-从db{SSHA512}散列中获取散列
-在开头临时附加一个SSHA512$
字符串,然后进行检查
因此,当您的数据库中有{SSHA512}散列时
,当django使用此后端时,它将看到SSHA512${SSHA512}散列
这样,在您的hasher.py
中,您可以在类中设置algorithm=“SSHA512”
,这将提示django在这种情况下使用此hasher
您的def encode(self、password、salt、iterations=None)
方法在hasher.py
中将以dovecot需要{SSHA512}散列的方式保存散列(您不必在编码方法中做任何奇怪的事情)
但是,您的def verify(self,password,encoded)
方法必须从传递的编码字符串中去掉SSHA512$“技巧”,以将其与编码将创建的字符串进行比较
好了,给你!Django将使用您的哈希程序来检查不包含美元$符号的哈希,并且您不必在Django内部破坏任何内容:)虽然几年过去了,但我将我的解决方案放在这里以供将来参考
弗拉德部分正确;django.contrib.auth.hasher中的以下方法似乎迫使您使用包含美元符号的哈希格式来标记django用于决定使用哪个哈希程序的算法
def identify_hasher(encoded):
"""
Returns an instance of a loaded password hasher.
Identifies hasher algorithm by examining encoded hash, and calls
get_hasher() to return hasher. Raises ValueError if
algorithm cannot be identified, or if hasher is not loaded.
"""
# Ancient versions of Django created plain MD5 passwords and accepted
# MD5 passwords with an empty salt.
if ((len(encoded) == 32 and '$' not in encoded) or
(len(encoded) == 37 and encoded.startswith('md5$$'))):
algorithm = 'unsalted_md5'
# Ancient versions of Django accepted SHA1 passwords with an empty salt.
elif len(encoded) == 46 and encoded.startswith('sha1$$'):
algorithm = 'unsalted_sha1'
else:
algorithm = encoded.split('$', 1)[0]
return get_hasher(algorithm)
不过,有一种方法可以“欺骗”django,而不必对django安装进行黑客攻击。您必须创建用于身份验证的身份验证后端。在这里,您将覆盖django的check_password方法。我有一个数据库,其中散列是{SSHA512}hash
,我无法更改它,因为我必须能够与dovecot通信。因此,我在我的backends.py
类中添加了以下内容:
def check_password(self, raw_password, user):
"""
Returns a boolean of whether the raw_password was correct. Handles
hashing formats behind the scenes.
"""
def setter(raw_password):
user.set_password(raw_password)
user.save(update_fields=["password"])
return check_password(raw_password, "SSHA512$" + user.password, setter)
这样,当django必须检查密码是否正确时,它将执行以下操作:
-从db{SSHA512}散列中获取散列
-在开头临时附加一个SSHA512$
字符串,然后进行检查
因此,当您的数据库中有{SSHA512}散列时
,当django使用此后端时,它将看到SSHA512${SSHA512}散列
这样,在您的hasher.py
中,您可以在类中设置algorithm=“SSHA512”
,这将提示django在这种情况下使用此hasher
您的def encode(self、password、salt、iterations=None)
方法在hasher.py
中将以dovecot需要{SSHA512}散列的方式保存散列(您不必在编码方法中做任何奇怪的事情)
但是,您的def verify(self,password,encoded)
方法必须从传递的编码字符串中去掉SSHA512$“技巧”,以将其与编码将创建的字符串进行比较
好了,给你!Django将使用您的哈希程序检查不包含美元$符号的哈希,并且您不必在Django内部破坏任何内容:)danihp谢谢,但我在该线程中找不到身份验证和自定义pasword哈希程序的问题。我认为,自定义pasword哈希程序应该在没有自定义身份验证的情况下透明地使用身份验证。因此,我认为问题出在我的代码中的某个地方。你能重复同样的操作吗,但在验证前后都要打印u.password吗?谢谢,但我在该线程中找不到验证和自定义pasword哈希程序的问题。我认为,自定义pasword哈希程序应该在没有自定义身份验证的情况下透明地使用身份验证。因此,我认为问题出在我的代码中的某个地方。请您重复相同的操作,但在验证前后是否打印u.password?