В данном руководстве будет разобрано использование PBKDF2_HMAC
для шифрования паролей вместе с солью в Python.
Содержание статьи
- Зачем нужно хешировать пароли?
- Почему не стоит использовать SHA-256 или нечто похожее
- Хеширование паролей с pbkdf2_hmac
- Генерация соли в Python
- Хеширование в Python
- Хранение хеша и соли
- Проверка правильности пароля в Python
- Пример регистрации пользователя с проверкой пароля
Зачем нужно хешировать пароли?
Во время аутентификации пользователей и прочего с помощью паролей никогда не сохраняйте пароль в виде открытого текста (plaintext). Если злоумышленник найдет базу данных паролей в незашифрованном виде, их можно легко использовать в сочетании с соответствующими адресами электронной почты для входа на сайт/аккаунт и даже использовать для попытки входа на другие аккаунты, поскольку многие люди часто используют один и тот же пароль везде.
Популярный метод, используемый сегодня, заключается в хешировании паролей, когда они предоставлены во время регистрации. Рекомендуется использовать при хешировании соль и хранить ее с хешированным паролем.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Почему не стоит использовать SHA-256 или нечто похожее
Хеш алгоритмы безопасности являются односторонними функциями, то есть после хеширования открытого текста нельзя получить исходный текст из хеша. Это хорошо, так как пароль остается скрытым, также можно выполнить простую проверку путем хеширования пароля, предоставленного пользователем, и сравнить его с сохраненным хешем фактическим паролем.
К сожалению, алгоритмы хеширования, такие как SHA-256, очень быстро вычисляются. Это значит, что многие комбинации строк можно быстро рассчитать и сопоставить с конкретным хешем. Если злоумышленник получил хеш паролей, которые были захешированы чем-то вроде SHA-256, он может попытаться сгенерировать все возможные пароли и хешировать их, чтобы найти совпадение для хеша паролей. Это называется грубой силой, или брутфорс.
Из-за масштаба поиска для большинства паролей, сегодня это использовать не очень практично. Будет лучше задействовать перебор по словарю, то есть подход меньшего подмножества. Проще говоря, рассматриваются ранее созданные файл/база данных, что потенциально содержат нужные пароли. Получается, что здесь пароль угадывается, нежели генерируется любая возможная комбинация. Еще одна тактика сопоставления хешей — использование радужных таблиц, в которых задействован групповой подход к случайному генерированию паролей.
Хеширование паролей с pbkdf2_hmac
Одним из достоинств методов сопоставления является использование более медленного метода хеширования. Это значит, что для вычисления множества хешей за определенный промежуток времени потребуется больше времени. Получается, поиск становится нереальном долгим, и на обнаружение совпадения может не хватить всей вашей жизни.
PBKDF2 является функцией выведения ключей, где пользователь может устанавливать вычислительные затраты. Это нужно для замедления вычисление ключа, чтобы сделать его более непрактичным для его взлома через брутфорс. Короче говоря, для получения определенной длины ключа требуется пароль, соль и число или итерации, которые можно сравнить с хешем, поскольку это также односторонняя функция хеширования.
При большом числе итераций алгоритм вычисляет конечный результат дольше. Это совершенно нормально для тех, кому нужно сделать только одну-две попыток проверки верности пароля, однако для миллиарда попыток потребуют очень много времени.
Обратите внимание, что использование метода PBKDF2 не останавливает атаки грубой силы/перебора по словарю или использование радужных таблиц, а просто делает методы более сложными в вычислительном отношении.
PBKDF2_HMAC является реализацией функции получения ключа PBKDF2, использующей HMAC в качестве псевдослучайной функции. pbkdf2_hmac
можно найти в библиотеке hashlib (которая поставляется с Python) и находится в Python 3.4 и выше. pbkdf2_hmac
принимает пять параметров:
hash_name
: алгоритм хеш дайджеста для HMAC;password
: пароль, превращенный в ключ;salt
: случайно сгенерированная соль;iterations
: итерации в вычислении (чем больше, тем длиннее вычисления);dklen
: длина ключа вывода (не обязательно).
Генерация соли в Python
Перед генерацией ключа с использованием pbkdf2_hmac
нужно сгенерировать случайную соль. Соль увеличивает затраты поиска в случае грубой силы, а также усложняет работу для радужных таблицы. Использование соли требует чуть больше работы и хранении дополнительной последовательности байтов.
Соль не нужно скрывать, шифровать или хешировать. Она просто складывается с паролем, чтобы ввод покрывал больший диапазон. Комбинацию осуществляет pbkdf2_hmac
, так что вам не нужно делать это самостоятельно.
Для генерации соли используется функция os.urandom, которая возвращает случайные байты, используемые для шифрования. Данная функция не использует генераторы псевдослучайных чисел, поэтому возвращаемое значение нельзя предугадать. Как раз то, что нам нужно.
1 2 3 |
import os salt = os.urandom(32) |
Параметр 32
является размером, возвращаемым в байтах. Можно выбрать любой размер, но лучше не ставить значение более 16 байт.
Вывод отсюда будет использован в pbkdf2_hmac
, после чего сохранен рядом с ключом вывода (мы будем использовать его как хеш) из pbkdf2_hmac
. Каждый пароль, относящийся к пользователю/объекту, должен иметь свой собственный хеш, не используйте один и тот же хеш для всех паролей пользователя/объекта.
Хеширование в Python
После ознакомления с основными концепциями можем запустить определенный код. Лучше всего учиться на примерах, поэтому разберем следующий пример:
1 2 3 4 5 6 7 8 9 10 11 |
import hashlib import os salt = os.urandom(32) # Запомните password = 'password123' key = hashlib.pbkdf2_hmac( 'sha256', # Используемый алгоритм хеширования password.encode('utf-8'), # Конвертируется пароль в байты salt, # Предоставляется соль 100000 # Рекомендуется использовать хотя бы 100000 итераций SHA-256 |
Так как длина ключа была предоставлена, используется размер дайджеста хеш алгоритма. В данном случае мы используем SHA-256, поэтому размер будет 64 байтов. Если нужен более длинный ключ для использования данного ключа в AES, требуется передать желаемый размер ключа dklen
после итерации в hashlib.pbkdf2_hmac
. Рассмотрим пример:
1 2 3 4 5 6 7 8 9 10 11 12 |
import hashlib import os salt = os.urandom(32) # Запомните это password = 'password123' key = hashlib.pbkdf2_hmac( 'sha256', # Используемый алгоритм хеширования password.encode('utf-8'), # Конвертирование пароля в байты salt, # Предоставление соли 100000, # Рекомендуется использоваться по крайней мере 100000 итераций SHA-256 dklen=128 # Получает ключ в 128 байтов |
В данном примере ключом является ваш «хеш».
Хранение хеша и соли
Из указанных выше фрагментов нужно сохранить соль и ключ. Для хранения можно использовать методы JSON, SQL, CSV и даже простой текстовый файл. Убедитесь, что пароль нигде не сохранен, ведь в этом состоит главная цель — избежать необходимость сохранения действующего пароля в исходном виде.
Если вы ограничены только одним местом для хранения, можете добавить соль и пароль вместе и потом сохранить их. Можно будет разделить их во время чтения, когда вам будет известна длина соли и ключа. Рассмотрим пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import os import hashlib # Пример генерации salt = os.urandom(32) key = hashlib.pbkdf2_hmac('sha256', 'mypassword'.encode('utf-8'), salt, 100000) # Хранение как storage = salt + key # Получение значений обратно salt_from_storage = storage[:32] # 32 является длиной соли key_from_storage = storage[32:] |
Если можно использовать два места (в большинстве случаев так и есть), используйте. Это значительно облегчает процесс.
Проверка правильности пароля в Python
После того, как пользователь впервые предоставил свой пароль, и вы сгенерировали для него соль, вычислили ключ, используя пароль и соль, а затем сохранили этот пароль и соль, можно проверить правильность дальнейших паролей.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import hashlib salt = b'' # Получение соли, сохраненной для *этого* пользователя key = b'' # Получение рассчитанного ключа пользователя password_to_check = 'password246' # Пароль, предоставленный пользователем, проверяется # Используется та же настройка для генерации ключа, только на этот раз вставляется для проверки настоящий пароль new_key = hashlib.pbkdf2_hmac( 'sha256', password_to_check.encode('utf-8'), # Конвертирование пароля в байты salt, 100000 ) if new_key == key: print('Пароль правильный') else: print('Пароль неправильный') |
При использовании приведенного выше фрагмента кода для генерации первого ключа в выводе будет значиться Пароль неправильный
. Все верно, ведь пароли и правда не совпадают. При повторном запуске, но с правильным паролем на этот раз скрипт должен вывести Пароль правильный
, ведь на этот раз пароли совпадают.
Пример регистрации пользователя с проверкой пароля
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
import hashlib import os users = {} # Простое демо хранилище # Add a user username = 'Brent' # Имя пользователя password = 'mypassword' # Пароль пользователя salt = os.urandom(32) # Новая соль для данного пользователя key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) users[username] = { # Хранение ключа и соли 'salt': salt, 'key': key } # Попытка проверки 1 (неправильный пароль) username = 'Brent' password = 'notmypassword' salt = users[username]['salt'] # Получение соли key = users[username]['key'] # Получение правильного ключа new_key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) assert key != new_key # Ключи не совпадают, следовательно, пароли не совпадают # Попытка проверки 2 (правильный пароль) username = 'Brent' password = 'mypassword' salt = users[username]['salt'] key = users[username]['key'] new_key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) assert key == new_key # Ключи совпадают, следовательно, и пароли совпадают # Добавление нового пользователя username = 'Jarrod' password = 'my$ecur3p@$$w0rd' salt = os.urandom(32) # Новая соль для данного пользователя key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) users[username] = { 'salt': salt, 'key': key } # Проверяем правильно ли введен пароль username = 'Jarrod' password = 'my$ecur3p@$$w0rd' salt = users[username]['salt'] key = users[username]['key'] new_key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) assert key == new_key # Ключи совпадают, поэтому совпадают пароли и у этого пользователя |
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»