Как хранить пароли python
Перейти к содержимому

Как хранить пароли python

  • автор:

Модуль secrets в Python, генерация паролей и токенов

Генерация паролей, токенов и криптографически сильных случайных чисел

Модуль secrets используется для генерации криптографически сильных случайных чисел, подходящих для управления такими данными, как пароли, аутентификация учетной записи, токены безопасности и связанные секреты.

В частности, secrets следует использовать по умолчанию вместо генератора псевдослучайных чисел модуля random , который в свою очередь предназначен для моделирования и симуляции, а не для защиты или криптографии.

Сколько байтов следует использовать токенам?

Чтобы обезопасить себя от атак методом «перебора», токены должны иметь достаточную случайность. С 2015 года считается, что 32 байта (256 бит) является достаточно сильным токеном для типичного варианта использования.

Что бы указать собственную длину токена, то можно явно передать функциям token_* аргумент nbytes , который имеет тип int . Этот аргумент принимается за количество байт, которое будет использовано при создании токена.

В противном случае, если аргумент nbytes не предоставлен или равен None , функциям token_* будут использовать разумное значение по умолчанию.

Примечания:

  • Значение по умолчанию может быть изменено в любое время, в том числе во время выпусков обновлений.
  • Приложения не должны хранить пароли в восстанавливаемом формате, будь то простой или зашифрованный текст. Они должны быть хешированы с использованием криптографически сильной односторонней (необратимой) хеш-функции.
Примеры использования:

Создание восьмибуквенного буквенно-цифрового пароля:

>>> import string >>> import secrets >>> alphabet = string.ascii_letters + string.digits >>> password = ''.join(secrets.choice(alphabet) for i in range(8)) >>> password # '9vfMa4Wz' 

Создание десятисимвольного буквенно-цифрового пароля, содержащего как минимум один символ нижнего регистра, как минимум один символ верхнего регистра и как минимум три цифры:

import string import secrets alphabet = string.ascii_letters + string.digits while True: password = ''.join(secrets.choice(alphabet) for i in range(10)) if (any(c.islower() for c in password) and any(c.isupper() for c in password) and sum(c.isdigit() for c in password) >= 3): break print(password) # 'oz2B4b3dO9' 

Создание кодовой фразы в стиле «XKCD»:

import secrets # В стандартных системах Linux используйте удобный файл словаря. # Другие платформы, должны предоставить свой собственный список слов. with open('/usr/share/dict/words') as f: words = [word.strip() for word in f] password = ' '.join(secrets.choice(words) for i in range(4)) print(password) # "Np escape guidebook's outperforms" 

Создание временного, трудно угадываемого URL-адреса, содержащего маркер безопасности и подходящего для приложений восстановления пароля:

>>> import secrets >>> url = 'https://mydomain.com/reset=' + secrets.token_urlsafe() >>> url # 'https://mydomain.com/reset=l6nrqcxpqgoogE0TBjAQNHrrgm-VycP3gcbP4eqvFsM' 
  • КРАТКИЙ ОБЗОР МАТЕРИАЛА.
  • Случайные числа модуля secrets
  • Генерация токенов модулем secrets

Как не продолбать пароли в Python скриптах

Хранение паролей всегда было головной болью. В классическом варианте у вас есть пользователь, который очень старается не забыть жутко секретный «qwerty123» и информационная система, которая хранит хеш от этого пароля. Хорошая система еще и заботливо солит хеши, чтобы отравить жизнь нехорошим людям, которые могут украсть базу с хешированными паролями. Тут все понятно. Какие-то пароли храним в голове, а какие-то засовываем в зашифрованном виде в keepass.

Все меняется, когда мы убираем из схемы человека, который старательно вводит ключ с бумажки. При взаимодействии двух информационных систем, на клиентской стороне в любом случае должен храниться пароль в открытом для системы виде, чтобы его можно было передать и сравнить с эталонным хешем. И вот на этом этапе админы обычно открывают местный филиал велосипедостроительного завода и начинают старательно прятать, обфусцировать и закапывать секретный ключ в коде скриптов. Многие из этих вариантов не просто бесполезны, но и опасны. Я попробую предложить удобное и безопасное решение этой проблемы для python. И чуть затронем powershell.

Как делать не надо

Всем знакома концепция «временного скриптика». Вот буквально только данные по-быстрому распарсить из базы и удалить. А потом внезапно выясняется, что скрипт уже из dev-зоны мигрировал куда-то в продакшен. И тут начинают всплывать неприятные сюрпризы от изначальной «одноразовости».

Чаще всего встречается вариант в стиле:

db_login = 'john.doe' password = 'password!' 

Проблема в том, что здесь пароль светится в открытом виде и достаточно просто обнаруживается среди залежей старых скриптов автоматическим поиском. Чуть более сложный вариант идет по пути security through obscurity, с хранением пароля в зашифрованном виде прямо в коде. При этом расшифровка обратно должна выполняться тут же, иначе клиент не сможет предъявить этот пароль серверной стороне. Такой способ спасет максимум от случайного взгляда, но любой серьезный разбор кода вручную позволит без проблем вытащить секретный ключ. Код ниже спасет только от таких «shoulder surfers»:

  • KDE4 & KDE5 KWallet (требуется dbus)
  • Freedesktop Secret Service — множество DE, включая GNOME (требуется secretstorage)
  • Windows Credential Locker
  • macOS Keychain
  1. Похитить сам код (легко)
  2. Деобфусцировать при необходимости (легко)
  1. Похитить сам код (легко)
  2. Деобфусцировать при необходимости (легко)
  3. Скомпрометировать локальную машину, залогинившись под атакуемым пользователем (сложно)

Безопасный ввод пароля

Еще один частый вариант утечки секретных паролей — история командной строки. Использование стандартного input здесь недопустимо:

age = input("What is your age? ") print "Your age is: ", age type(age) >>output What is your age? 100 Your age is: 100 type 'int'> 

В примере выше я уже упоминал библиотеку getpass:

# Безопасно запрашиваем ввод пароля в CLI password = getpass.getpass(prompt="Enter secret password:") 

Ввод данных при ее использовании аналогичен классическому *nix подходу при входе в систему. Ни в какие системные логи данные не пишутся и не отображаются на экране.

Немного о Powershell

Для Powershell правильным вариантом является использование штатного Windows Credential Locker.
Реализуется это модулем CredentialManager.

Install-Module CredentialManager -force New-StoredCredential -Target $url -Username $ENV:Username -Pass . Get-StoredCredential -Target . 

Как хешировать пароли в Python

hashlib сохранения пароля

В данном руководстве будет разобрано использование PBKDF2_HMAC для шифрования паролей вместе с солью в Python.

Зачем нужно хешировать пароли?

Во время аутентификации пользователей и прочего с помощью паролей никогда не сохраняйте пароль в виде открытого текста (plaintext). Если злоумышленник найдет базу данных паролей в незашифрованном виде, их можно легко использовать в сочетании с соответствующими адресами электронной почты для входа на сайт/аккаунт и даже использовать для попытки входа на другие аккаунты, поскольку многие люди часто используют один и тот же пароль везде.

Популярный метод, используемый сегодня, заключается в хешировании паролей, когда они предоставлены во время регистрации. Рекомендуется использовать при хешировании соль и хранить ее с хешированным паролем.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по 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, которая возвращает случайные байты, используемые для шифрования. Данная функция не использует генераторы псевдослучайных чисел, поэтому возвращаемое значение нельзя предугадать. Как раз то, что нам нужно.

Безопасное управление паролями Python: хеширование и шифрование

При создании приложения, которое проверяет пароли пользователей или требует хранения токенов для будущего использования, крайне важно не хранить эти значения где-либо в виде открытого текста. Если есть нарушение безопасности, вы хотите быть уверены, что данные вашего пользователя защищены. Хеширование и шифрование — это несколько методов, которые можно использовать для достижения этой цели, и мы рассмотрим, как реализовать их с помощью Python.

Хеширование

Если вашему приложению необходимо разрешить пользователям регистрировать учетную запись и создавать пароль, вам необходимо сохранить значения, с которыми они подписались, чтобы позже аутентифицировать их. Вместо хранения паролей в открытом виде следует использовать алгоритм хеширования. Алгоритмы хеширования представляют собой односторонние функции, которые дают один и тот же результат для входных данных, но, учитывая, что выходные данные практически невозможно обратить вспять. Существует много типов хеш-алгоритмов, но SHA-256 — это надежный современный алгоритм, одобренный NIST, который соответствует потребностям большинства приложений с точки зрения надежности и производительности.

Создайте простой файл сценария Python, который будет принимать входные данные и генерировать хэш SHA-256 с помощью стандартной библиотеки hashlib.

import hashlib password = input("Password: ") password_hash = hashlib.sha256(password.encode("utf-8")).hexdigest() print(f"Password Hash: ")

Запустите скрипт и введите несколько паролей.

$ python3 hash.py Password: test123 Password Hash: ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae $ python3 hash.py Password: test123 Password Hash: ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae $ python3 hash.py Password: test1234 Password Hash: 937e8d5fbb48bd4949536cd65b8d35c426b80d2f830c5c308e2cdec422ae2244

Мы видим, что одни и те же входные данные дают один и тот же результат хеширования, но любое изменение, например добавление дополнительного символа, полностью меняет его. Полученное хэш-значение — это то, что вы должны сохранить в своей базе данных, чтобы позже проверить пароль пользователя.

Шифрование

Хеширование — отличный вариант, когда вам не нужно использовать значение пароля. В случаях, когда вам необходимо использовать фактическое значение пароля, например, для хранения токена долгосрочного доступа для аутентификации от имени пользователя во внешнем приложении, лучшим вариантом является шифрование. Шифрование позволяет безопасно хранить значения и расшифровывать их в памяти, когда вам нужно их использовать. Для этого в Python есть библиотека криптографии, которая включает в себя симметричное шифрование Fernet. Симметричное шифрование означает, что у нас будет секретный ключ, который мы сможем хранить в переменных среды и использовать для расшифровки сохраненных значений.

Установите библиотеку cryptography и dotenv .

$ poetry add cryptography python-dotenv Using version ^41.0.5 for cryptography Using version ^1.0.0 for python-dotenv Updating dependencies Resolving dependencies. (0.1s) Package operations: 4 installs, 0 updates, 0 removals • Installing pycparser (2.21) • Installing cffi (1.16.0) • Installing cryptography (41.0.5) • Installing python-dotenv (1.0.0)

Запустите встроенную команду Python, чтобы сгенерировать секретный ключ Fernet.

$ python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key())" b'XvYvP_c4gBDLCLbjgz6Hc47ND_BcoMYt3Cz5pAKx1qQ='

Добавьте это значение в .env файл.

SECRET_KEY=XvYvP_c4gBDLCLbjgz6Hc47ND_BcoMYt3Cz5pAKx1qQ=

Создайте новый скрипт Python, который загрузит наш секретный ключ из переменных среды, создаст экземпляр клиента Fernet с этим ключом и позволит зашифровать и сохранить новый пароль в простом текстовом файле или распечатать расшифрованное значение существующего сохраненного пароля.

import os import sys from cryptography.fernet import Fernet from dotenv import load_dotenv load_dotenv() SECRET_KEY = os.getenv("SECRET_KEY") assert SECRET_KEY FERNET = Fernet(SECRET_KEY) if len(sys.argv) > 1 and sys.argv[1] == "decrypt": with open("pw.txt") as f: stored_password = f.read() stored_dec_password = FERNET.decrypt(stored_password).decode() print(f"Decrypted Password: ") else: new_password = input("New Password: ") new_enc_password = FERNET.encrypt(new_password.encode()).decode() with open("pw.txt", "w") as f: f.write(new_enc_password) print(f"Encrypted Password Stored: ")

Проверьте его, чтобы убедиться, что он работает должным образом.

$ python3 encrypt.py New Password: Test123!! Encrypted Password Stored: gAAAAABlR7V0TLTZMT_ZHEoPtqbW3B9LYgohYdUNG6Lukx9M2NSLgrFN6MUZKCNPP3Hq_KuuEPpJPPqqIktUkZTBh3qenKnQAA== $ python3 encrypt.py decrypt Decrypted Password: Test123!!

Здесь показаны основные концепции шифрования/дешифрования значений в производственной среде: вместо хранения их в простом текстовом файле вы просто сохраняете и извлекаете значения из базы данных.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *