Python crypt module-как правильно использовать соли?

во-первых, контекст: я пытаюсь создать инструмент на основе командной строки (Linux), который требуется логин. Учетные записи на этом инструменте не имеют ничего общего с учетные записи системного уровня - ничто из этого не смотрит на /etc / passwd.

Я планирую хранить учетные записи пользователей в текстовом файле, используя тот же формат (примерно), что и /etc/passwd.

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

мои знания крипты основаны на этом: https://docs.python.org/2/library/crypt.html

документация, похоже, просит что-то, что невозможно: "это рекомендуется использовать полный зашифрованный пароль в качестве соли при проверке для пароля."

да? Если я создаю зашифрованный пароль (например, при создании пользователя Запись) Как я могу использовать зашифрованный пароль как соль? Он еще не существует. (Я предполагаю, что вы должны использовать ту же соль для создания и проверки пароля.)

Я попытался использовать пароль открытого текста в качестве соли. Это работа, но имеет две проблемы: одну легко преодолеть, а другую серьезно:

1) Первые две буквы пароля открытым текстом включены в кодировка пароля. Вы можете исправить это, не написав первые два символы в файле:

user_record = '%s:%s:%s' % (user_name, crypted_pw[2:], user_type)

2) By используя пароль открытого текста в качестве соли, вы, похоже, уменьшение количества энтропии в системе. Возможно, я непонимание назначения соли.

лучшая практика, которую я смог получить, - использовать первые два символы из имени пользователя как соль. Будет ли это уместно, или есть что-то, что я пропустил, что делает этот шаг плохим?

мое понимание соли заключается в том, что она предотвращает предварительный вычислительный пароль хэши из словаря. Я можно использовать стандартную соль для всех пароли (например, мои инициалы "JS"), но это, кажется, меньше нагрузка для злоумышленника, чем использование двух символов из имени пользователя каждого пользователя.

8 ответов


для использования модуля crypt:

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

Общие замечания:

Если это не имеет ничего общего с фактической системой логины, ничто не мешает вам использовать более сильный метод, чем crypt. Вы можете случайным образом генерировать N символов соли для каждого пользователя, которые будут объединены с паролем пользователя в хэше SHA-1.

string_to_hash = user.stored_salt + entered_password
successful_login = (sha1(string_to_hash) == user.stored_password_hash)

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


crypt () Python-это оболочка для функции crypt() системы. Из справочной страницы Linux crypt ():

char *crypt(const char *key, const char *salt);

key is a user’s typed password.
salt is a two-character string chosen from the set [a–zA–Z0–9./]. 
This string is used to perturb the algorithm in one of 4096 
different ways.

акцент на "двух символов строка". Теперь, если вы посмотрите на поведение crypt () в Python:

>>> crypt.crypt("Hello", "World")
'Wo5pEi/H5/mxU'
>>> crypt.crypt("Hello", "ABCDE")
'AB/uOsC7P93EI'

вы обнаруживаете, что первые два символа результата всегда совпадают с первыми двумя символами исходной соли, которые действительно образуют истинную двухсимвольную соль. То есть результат crypt() имеет вид 2char-соль + шифрование-пасс. Следовательно, нет никакой разницы в результате, если вместо передачи двух символов-соли или исходного много символов-соли вы передаете весь зашифрованный пароль.

Примечание: набор [a-zA–Z0-9.содержит 64 символа 64*64=4096. Вот как два персонажи относятся к "4096 - разному".


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

ваша идея иметь начальную соль основана на имени пользователя, кажется, в порядке.


вот несколько общих советов по солению паролей:

  1. вообще, соли использованы для того чтобы сделать таблицы радужная слишком дорого, чтобы вычислить. Итак, вы должны добавить немного рандомизированной соли ко всем хэшам паролей и просто сохранить его в открытом тексте рядом со значением хэшированного пароля.
  2. использовать HMAC - это хороший стандарт, и он более безопасен, чем объединение пароля и соли.
  3. использовать SHA1: MD5 сломан. Без обид намеренно, если бы вы знали это, просто тщательно. ;)

Я не будет имейте соль быть функцией пароля. Злоумышленник должен был бы создать таблицу rainbow, чтобы иметь базу данных паролей мгновенного поиска, но им нужно было бы сделать это только один раз. Если вы выберете случайное 32-разрядное целое число, им придется генерировать 2^32 таблицы, которые (в отличие от детерминированной соли) стоят слишком много памяти (и времени).


для некоторой дополнительной силы вы можете заставить модуль crypt использовать md5, используя соль в формате.

$ABCDEFGH$

где ABCDEFGH ваша строка соли.

>>> p = crypt.crypt('password', '$s8Ty3/f$')
>>> p
Out: '$s8Ty3/fH/M0JswK9pl3X/e.n55G1'
>>> p == crypt.crypt('password', p)
Out: True

(обратите внимание, что это расширение gnu для crypt, см. "Man crypt" в системе linux). MD5 (и теперь даже SHA1) может быть "сломан", но они все еще относительно хороши для хэшей паролей, а md5 по-прежнему является стандартом для локальных паролей linux.


пароль или что-либо, полученное из пароля, никогда не должно использоваться в качестве соли. Соль для конкретного пароля должна быть непредсказуемой.

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


используйте PBKDF2, см. комментарий в другом потоке (включая реализацию Python).


взгляните на статью TrueCrypt и пояснил, по Edström Бьерн. Он содержит простое для понимания объяснение того, как работает truecrypt и простая реализация Python некоторых функций truecrypt включая Управление паролями.

Он говорит о модуле Python crypt (), а не о TrueCrypt в Python

по умолчанию crypt.crypt() в Python 2 не очень безопасно и статьи объясняет как могут работать более безопасные альтернативы.