Модуль Python crypt - как правильно использовать соли? - PullRequest
9 голосов
/ 01 декабря 2008

Во-первых, контекст: я пытаюсь создать инструмент на основе командной строки (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) При использовании открытого текста в качестве соли, вы, казалось бы, уменьшение количества энтропии в системе. Возможно я недопонимание назначения соли.

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

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

Ответы [ 8 ]

7 голосов
/ 01 декабря 2008

Python crypt () - это оболочка для системной функции 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-salt + encrypted-pass. Следовательно, нет никакой разницы в результате, если вместо передачи двухсимвольной соли или исходной многозначной соли вы передаете весь зашифрованный пароль.

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

4 голосов
/ 01 декабря 2008

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

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

Общие комментарии:

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

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

ОБНОВЛЕНИЕ: Хотя это гораздо более безопасно для радужных таблиц, метод, описанный выше, все еще имеет криптографические недостатки. Правильное применение алгоритма HMAC может еще больше повысить вашу безопасность, но выходит за рамки моей компетенции.

3 голосов
/ 01 декабря 2008

Вот несколько общих советов по соляным паролям:

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

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

3 голосов
/ 01 декабря 2008

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

Ваша идея о том, чтобы исходная соль основывалась на имени пользователя, кажется нормальной.

2 голосов
/ 01 декабря 2008

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

$1$ABCDEFGH$

где ABCDEFGH - ваша соленая нить.

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

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

1 голос
/ 01 декабря 2008

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

1 голос
/ 01 декабря 2008

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

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

0 голосов
/ 07 декабря 2008

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

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

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

...