2012-12-29 20 views
1

進行身份驗證我正在使用php將一個網站遷移到Django框架。無法通過自定義PASSWORD_HASHERS

有用於特定的哈希密碼算法,所以我不得不寫:

#settings.py 
PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.PBKDF2PasswordHasher', 
    'project.hashers.SHA1ProjPasswordHasher',  # that's mine 
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 
... 
) 

和:

#hashers.py 

import hashlib 

from django.contrib.auth.hashers import (BasePasswordHasher, mask_hash) 
from django.utils.datastructures import SortedDict 
from django.utils.encoding import force_bytes 
from django.utils.crypto import constant_time_compare 
from django.utils.translation import ugettext_noop as _ 


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): 
     return hashlib.sha1(force_bytes(salt + password)).hexdigest()[:8] 

    def verify(self, password, encoded): 
     encoded_2 = self.encode(password, '') 
     return constant_time_compare(encoded, encoded_2) 

    def safe_summary(self, encoded): 
     return SortedDict([ 
      (_('algorithm'), self.algorithm), 
      (_('hash'), mask_hash(encoded, show=3)), 
      ]) 

它的效果很好,當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' 

可能是什麼問題?謝謝。


更新:好的,問題變得更加清晰。當我從這裏取出切片:

return hashlib.sha1(force_bytes(salt + password)).hexdigest()[:8] 

一切工作正常。我不知道爲什麼..

+0

danihp謝謝,但我找不到驗證和自定義pasword哈希器的問題在那個線程中。 我認爲,自定義口令散列器應該透明地與認證一起工作,而不需要自定義認證。 所以,我認爲這個問題在我的代碼中。 – vlad

+0

您能否重複相同的操作,但在進行身份驗證之前和之後執行'打印u.password'? –

+0

已在帖子中添加。 – vlad

回答

0

只有無鹽的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)), 
      ]) 

>>> from django.contrib.auth import authenticate 
>>> x = authenticate(username='root', password='test') 
>>> x 
<User: root> 
>>> x.password 
u'unsalted_and_trimmed_sha1$$a94a8fe5' 
1

雖然多年過去了,我可是把我的解決方案供將來參考

弗拉德是部分正確的;從django.contrib.auth.hashers下面的方法似乎是強迫你使用哈希格式,包括美元$符號來標記用於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必須檢查這樣如果密碼是正確的,它會執行以下操作:暫時 - 獲得從數據庫的散列{SSHA512}hash -append它一個SSHA512$串上開始,然後檢查

所以,當你當Django使用該後端它會看到SSHA512${SSHA512}hash可以在你的數據庫,{SSHA512}hash

這種方式在你的hashers.py中,你可以在你的課algorithm = "SSHA512"中設置,這將提示django在這種情況下使用此散列器。

你的def encode(self, password, salt, iterations=None)方法在你的hashers.py將節省散列dovecot需要{SSHA512}散列的方式(你不必在你的編碼方法中做任何奇怪的事情)。

您的def verify(self, password, encoded)方法雖然將不得不從編碼的字符串中去掉SSHA512 $「技巧」,然後將其與編碼將要創建的編碼字符串進行比較。

所以你有它! Django將使用你的哈希器來檢查不含$美元符號的哈希值,並且你不必在django裏面打破任何東西:)

相關問題