Как я могу использовать функцию check_password_hash werkzeug.security для проверки правильности пароля по существующим хешам с соленым паролем sha1 - PullRequest
2 голосов
/ 18 октября 2019

Я работаю над новым приложением python для работы, которая должна проверить существующие учетные данные пользователя в нашей базе данных. Многие из наших реликтовых приложений были построены на основе PHP-среды Symfony, откуда и произошли эти хэши и соли паролей. В базе данных сохраняется алгоритм (sha1), соль и хешированный пароль для пользователей. Я пытаюсь выяснить, как правильно проверить пароль, используя эту информацию в сочетании с функцией check_password_hash модуля werkzeug.security. Тем не менее, до сих пор я потерпел неудачу.

Для демонстрационных целей я создал тестового пользователя, и база данных содержит следующие значения для пользователя.

Проверка пользовательских значений

algorithm: sha1
salt: e40e1e9addc186828a5554a71527342c
password (хэшировано): 784517f57fbe61179960739e29d7ae925aa4f * **5 1015 * 56051017 * фактический пароль для тестового пользователя - 123456

Для проверки пароля использовались следующие методы.

Примечания:
1. Формат хэша был адаптирован из документации werkzeug.security
2. Метод сравнительного хэширования был адаптирован из этого ответа StackOverflow

from werkzeug.security import generate_password_hash
from werkzeug.security import check_password_hash

# Attempt 1 - Results in False
check_password_hash('sha1$e40e1e9addc186828a5554a71527342c$784517f57fbe61179960739e29d7ae925aa4fd5b','123456')

# Attempt 2 - Results in False
check_password_hash('pbkdf2:sha1$e40e1e9addc186828a5554a71527342c$784517f57fbe61179960739e29d7ae925aa4fd5b','123456')

# Attempt 3
# Step 1: Append the salt_value to the given password and hash it using the same hash function.
generate_password_hash('123456$e40e1e9addc186828a5554a71527342c','sha1')
# sha1$9lUceSsd$60f7dcb3ff9c22d4613e59fcbfed0c463ee4189e
# Step 2: Compare the hash to the hash in the database
# 60f7dcb3ff9c22d4613e59fcbfed0c463ee4189e != 784517f57fbe61179960739e29d7ae925aa4fd5b

# Attempt 4 - Same steps as Attempt 3 except adding salt_length argument
# Step 1: Append the salt_value to the given password and hash it using the same hash function.  
generate_password_hash('123456$e40e1e9addc186828a5554a71527342c','sha1',len('e40e1e9addc186828a5554a71527342c'))
# sha1$EbPv6DP0wMyu02UpA6ZYazFvvYvZTVI1$b8252583fea027d42af20c0d0f3eac3fbf468bd1
# Step 2: Compare the hash to the hash in the database
# b8252583fea027d42af20c0d0f3eac3fbf468bd1 != 784517f57fbe61179960739e29d7ae925aa4fd5b

Может кто-нибудьдать некоторое представление о том, что я делаю неправильно? Стоит ли использовать другую библиотеку, например passlib? Я надеялся, что этого модуля будет достаточно, так как я знаком с ним, создав несколько наших существующих приложений на Python, которые используют его для новых пользовательских регистраций, но в отдельной базе данных.

Редактировать - показать альтернативный метод использования werkzeug.security 'check_password_hash function

import hashlib
# hashlib.sha1('{salt}{pw}'.encode()).hexdigest() == {pw_hash}
hashlib.sha1('e40e1e9addc186828a5554a71527342c123456'.encode()).hexdigest() == '784517f57fbe61179960739e29d7ae925aa4fd5b'
# True

1 Ответ

1 голос
/ 18 октября 2019

werkzeug.generate_password_hash хочет получить значение соли. Взглянув на источник, мы видим, что _hash_internal вызывается с сгенерированным значением соли.

def generate_password_hash(password, method="pbkdf2:sha256", salt_length=8):
    """Hash a password with the given method and salt with a string of
    the given length. The format of the string returned includes the method
    that was used so that :func:`check_password_hash` can check the hash.

    The format for the hashed string looks like this::

        method$salt$hash

    This method can **not** generate unsalted passwords but it is possible
    to set param method='plain' in order to enforce plaintext passwords.
    If a salt is used, hmac is used internally to salt the password.

    If PBKDF2 is wanted it can be enabled by setting the method to
    ``pbkdf2:method:iterations`` where iterations is optional::

        pbkdf2:sha256:80000$salt$hash
        pbkdf2:sha256$salt$hash

    :param password: the password to hash.
    :param method: the hash method to use (one that hashlib supports). Can
                   optionally be in the format ``pbkdf2:<method>[:iterations]``
                   to enable PBKDF2.
    :param salt_length: the length of the salt in letters.
    """
    salt = gen_salt(salt_length) if method != "plain" else ""
    h, actual_method = _hash_internal(method, salt, password)
    return "%s$%s$%s" % (actual_method, salt, h)

Если мы вызываем _hash_internal с помощью вашего sha1 метода и при условии соли / пароля, мы получаемдругой хеш

In [83]: import werkzeug.security                                                                                                                                                     

In [84]: h, method = werkzeug.security._hash_internal('sha1', 'e40e1e9addc186828a5554a71527342c', '123456')                                                                           

In [86]: h == '784517f57fbe61179960739e29d7ae925aa4fd5b'                                                                                                                              
Out[86]: False

In [85]: h                                                                                                                                                                            
Out[85]: 'e8c2de9bdc1ab92479e3e55b608a040dad7bf656'

Я думаю, что вам нужно пересмотреть ваш PHP-код, чтобы увидеть, как генерируются эти значения.

РЕДАКТИРОВАТЬ: согласно вашему комментарию

In [138]: werkzeug.security._hash_internal('sha1', '', 'e40e1e9addc186828a5554a71527342c123456')                                                                                      
Out[138]: ('784517f57fbe61179960739e29d7ae925aa4fd5b', 'sha1')

Если вы хотите использовать check_password_hash, вам не нужно указывать соль в вашем хэше, а вместо этого добавлять его к своему паролю:

In [148]: werkzeug.check_password_hash('sha1$$784517f57fbe61179960739e29d7ae925aa4fd5b', 'e40e1e9addc186828a5554a71527342c123456')                                                    
Out[148]: True

Если мы посмотрим на источник, то увидим, что check_password_hashизвлекает method, salt и hashval из pwhash, затем проверяет хэшированные password совпадения hashval. Я не знаком с хэшированием в PHP, но кажется, что пароль был «засолен», а затем хеширован без соли (в любом случае, что касается werkzeug.security).

def check_password_hash(pwhash, password):
    """check a password against a given salted and hashed password value.
    In order to support unsalted legacy passwords this method supports
    plain text passwords, md5 and sha1 hashes (both salted and unsalted).

    Returns `True` if the password matched, `False` otherwise.

    :param pwhash: a hashed string like returned by
                   :func:`generate_password_hash`.
    :param password: the plaintext password to compare against the hash.
    """
    if pwhash.count("$") < 2:
        return False
    method, salt, hashval = pwhash.split("$", 2)
    return safe_str_cmp(_hash_internal(method, salt, password)[0], hashval)
...