У меня есть приложение A, которое было сделано давно с python 2. Пароли имели такой формат в БД:
PBKDF2$sha256$10000$KlCW+ewerd19fS9f$l+5LgvcWTzghtz77086MSVG+q5z2Lij
В новой системе python 3 (назовем это app B) я настроил функции хеширования, и она всегда работает идеально, в новых системах пароли в БД выглядят так:
PBKDF2$sha256$10000$b'GAsfjwehd0x08C8'$b'w4oGw5vDjfgwerfweDjDFbwr3CoRrCkMOMwo9LOw1sWSg='
Теперь мне нужно переместить всех пользователей из приложения A в приложение B и сохранить пароли.
Первая ошибка, с которой я столкнулся после перемещения, была binascii error, invalid padding
, поэтому я просто добавил отступ:
value['password']
: PBKDF2$sha256$10000$KlCW+ewerd19fS9f$l+5LgvcWTzghtz77086MSVG+q5z2Lij
value['password'] += "=" * ((4 - len(value['password']) % 4) % 4)
После этого у меня было много разных попыток, и он всегда останавливался на:
Проблема:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x97 in position 0: invalid start byte
Я думаю, что лучше всего настроить формат старого пароля, чтобы он совпадал с новыми паролями (которые работают), поэтому я сделал это:
value['password'] = "PBKDF2$sha256$10000$" + str(value['password'].split('PBKDF2$sha256$10000$')[1].split('$')[0].encode('utf-8')) +\
"$" + str(value['password'].split('PBKDF2$sha256$10000$')[1].split('$')[1].encode('utf-8'))
Теперь старый пароль в БД выглядит так, как и новый PW:
PBKDF2$sha256$10000$b'KlCW+ewerd19fS9f'$b'$l+5LgvcWTzghtz77086MSVG+q5z2Lij=='
Новый check_function
, который выбрасывает UnicodeDecodeError
:
def check_hash(password, hash_):
"""Check a password against an existing hash."""
if isinstance(password, str):
password = password.encode('utf-8')
algorithm, hash_function, cost_factor, salt, hash_a = hash_.split('$')
assert algorithm == 'PBKDF2'
""".decode("utf-8") transforms a bytes obj to string in python 3"""
#actual_data += "=" * ((4 - len(actual_data) % 4) % 4)
#salt = salt.split("'")[1]
#hash_a = hash_a.split("'")[1]
salt = salt[2:][:-1]
hash_a = hash_a[2:][:-1]
password = password.decode("utf-8")
print(hash_a, type(hash_a))
print(salt, type(salt))
print(password, type(password))
hash_a = b64decode(hash_a)
print ('after b64decode', hash_a, type(hash_a))
# THE ERROR HAPPENS HERE: #
# THE ERROR HAPPENS HERE: #
# THE ERROR HAPPENS HERE: #
hash_a = hash_a.decode("utf-8")
print("after", hash_a, type(hash_a))
print ("actually check", bytes(salt, 'utf-8'))
hash_b = pbkdf2_bin(bytes(password, 'utf-8'),
bytes(salt, 'utf-8'),
int(cost_factor),
len(hash_a),
getattr(hashlib, hash_function))
print(len(hash_a),len(hash_b))
assert len(hash_a) == len(hash_b) # we requested this from pbkdf2_bin()
# Same as "return hash_a == hash_b" but takes a constant time.
# See http://carlos.bueno.org/2011/10/timing.html
diff = 0
for char_a, char_b in zip(hash_a, hash_b):
#print("char", char_a, type(char_a), char_b, zip(hash_a, hash_b))
diff |= ord(char_a) ^ ord(char_b)
return diff == 0
Вплоть до ошибки все отпечатки идентичны для новых рабочих паролей и для старого неработающего пароля, поэтому я ничего не понимаю. Все выглядит одинаково, но выдает ошибку.
Я пытался сделать его как можно короче, потому что я думаю, что проблема должна быть в частях, которые я опубликовал, я могу ошибаться. При необходимости я публикую полные сценарии Python 2 и Python 3, но это очень много.
Итак, подведем итог:
hash_a = hash_a.decode("utf-8")
работает для нового пароля, но не для старого, даже если оба имеют одинаковый формат и тип.
P.S .: под новым паролем я имею в виду пароли, которые были созданы с помощью нового скрипта, который я обновил до python 3.
P.S .: старые пароли были сгенерированы старым скриптом, который использовал python 2.