Помогите мне понять pack (), openssl_random_pseudo_bytes () и mt_rand () для подсчета паролей - PullRequest
7 голосов
/ 10 февраля 2011

Я создаю приложение, которое будет иметь пользовательскую базу, и я нахожусь в точке обеспечения входа в систему. Я довольно плохо знаком с программированием (и PHP), но мои усилия до сих пор указывали на использование Crypt() и хешированной соли Blowfish.

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

В документации crypt() пользователь недавно опубликовал это:

<?php 
   $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22); 
?>

Предназначен для использования в системах где mt_getrandmax () == 2147483647.

Созданная соль будет 128 бит длина, дополненная до 132 бит, а затем выражается в 22 base64 символов. (CRYPT_BLOWFISH использует только 128 бит для соль, хотя есть 132 биты в 22 символа base64. если ты изучить ввод CRYPT_BLOWFISH и вывод, вы можете видеть, что он игнорирует последние четыре бита на входе и устанавливает их к нулю на выходе.)

Обратите внимание, что старшие биты четыре 32-битных меча, возвращенные mt_rand () всегда будет нулевым (так как mt_getrandmax == 2 ^ 31), поэтому только 124 128 бит будут псевдослучайными. я нашел, что приемлемо для моего применение.

Я проверил свой сервер, и действительно, mt_getrandmax () возвращает 2147483647. Я попытался просмотреть документацию, чтобы понять, что на самом деле делает вышеуказанный код - код pack() N4 для 32-битной строки (порядок байтов с прямым порядком байтов) ??) повторил 4 раза ... что я предполагаю, поэтому есть 4 mt_rand() аргументы.

Я не понимаю, почему он заменяет + на . и назначает 22 символа base64 (не то, чтобы я полностью понимал, что такое base64.)

Было рекомендовано посмотреть на openssl_random_pseudo_bytes() для моего случайного образования соли, так как предыдущий метод, на который я смотрел, ограничивался только 1234567890abcdefghijklmnopqrstuvwxyz.

Предположительно, была ошибка до 5.3.4 , из-за которой openssl_random_pseudo_bytes() работал мучительно медленно, иногда вызывая ошибки тайм-аута. Я не уверен, стоит ли мне пытаться использовать openssl_random_pseudo_bytes() с Crypt() или что-то подобное описанному выше, используя mt_rand() и pack().

Я пытаюсь лучше понять, как работают все эти элементы и что они делают концептуально, а не просто использовать один из них, не понимая его, для достижения моей цели; Я пытаюсь научиться: P

Может кто-нибудь помочь мне понять различные элементы на работе здесь или, по крайней мере, направить меня в базу знаний, где я могу прочитать об этом? Я думаю, что наиболее скрытным компонентом является понимание различных форматов / терминологии (base64, ascii, hexdec, bit, byte и т. Д.), А также, в конце концов, как получить довольно безопасную соль для использования с моими паролями.

1 Ответ

11 голосов
/ 10 февраля 2011

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

Ваши конкретные вопросы

  1. Почему он заменяет + на .?

    Понятия не имею. Возможно, это связано с тем, что символ + можно спутать с пробелом в URL. Но соль никогда не должна быть в URL, так что, скорее всего, это не так.

  2. Что делает base64 / hexdec:

    Base64 преобразует необработанный поток байтов (каждый байт имеет значения от 0 до 255) в представление base 64. На нем много ресурсов, поэтому углубляться в него не стоит. Прочитайте статью в Википедии для получения дополнительной информации.

    hexdec преобразует шестнадцатеричное число (a-f0-9) в десятичное. Он преобразует из базы 16 в базу 10 (просто еще один способ представления чисел).

  3. Что такое бит и байт:

    Бит - это единица информации. Он имеет 2 состояния, 0 или 1. Байт представляет собой последовательность из 8 битов. Таким образом, байт может иметь 256 уникальных комбинаций. Читать Википедия ...

  4. Что такое ASCII

    Это набор символов. Он представляет один печатный символ в одном 8-битном байте. Снова, я бы предложил прочитать Википедию .

Соли вообще

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

Теперь вам нужно определить, какие символы являются приемлемыми для соли (поскольку вам нужно хранить соль для проверки хеша). Лучшими из возможных солей являются полнобайтовые числа, а не просто отображаемые символы. Теперь вы не сможете отобразить это в значимой усадьбе, но вам не нужно отображать это. Кроме того, для хранения вы всегда можете использовать base64_encode it.

Далее вам нужно выбрать, насколько большой вы хотите, чтобы соль была. Чем больше соль, тем лучше. Соль из 32 символов допустима, но соль из 128 символов лучше. Размер соли и количество опций на персонажа будут определять количество возможных вариантов. Некоторые общие комбинации:

Hex, 32 characters: 2e38 possibilities
Hex, 128 characters: 1e154 possibilities
Full Byte, 32 characters: 1e77 possibilities
Full Byte, 128 characters: 1e308 possibilities

Теперь вам нужно создать соль. Ключ должен сделать столько случайных вызовов, сколько необходимо для заполнения энтропии. Вы можете сделать это несколькими способами:

  • Зависит от системы (работает только на * nix, но лучше энтропии):

    $f = fopen('/dev/urandom', 'r');
    $seed = fgets($f, $characters); // note that this will always return full bytes
    fclose($f);
    
  • Зависит от библиотеки (хорошо, но требует установки OpenSSL)

    $seed = openssl_random_pseudo_bytes($characters);
    
  • запасной вариант

    $seed = '';
    for ($i = 0; $i < $characters; $i++) {
        $seed .= chr(mt_rand(0, 255));
    }
    

Теперь вам нужно преобразовать его в желаемый выходной формат.

  • Hex (a-f0-9):

    $out = '';
    for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
        $num = ord($seed);
        $out .= dechex(floor($num / 16)) . dechex($num % 16);
    }
    
  • Base36 (a-z0-9):

    $out = '';
    for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
        $num = ord($seed);
        $out .= base_convert($num, 10, 36);
    }
    
  • Base64 (a-zA-Z0-9 + =):

    $out = base64_encode($seed);
    
  • Полный байт:

    Ничего не нужно, поскольку он уже в этом формате.

...