Как получить хорошую соль - Достаточно ли безопасна моя функция? - PullRequest
36 голосов
/ 04 ноября 2010

Вот функция, которую я использую для генерации случайных солей:

function generateRandomString($nbLetters){
    $randString="";
    $charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for($i=0; $i<$nbLetters; $i++){
       $randInt=rand(0,61);
        $randChar=$charUniverse[$randInt];
        $randString=$randomString.$randChar;
    }
    return $randomString;
}

Это для некоммерческого сайта. Он используется только для генерации соли (хранится в БД и используется вместе с пользователем, переданным pw для хеширования).

Это уместно? Должен ли я использовать большее подмножество символов, и если так, есть ли простой способ сделать это в PHP?

Ответы [ 11 ]

41 голосов
/ 04 ноября 2010

Если вы хэшируете пароли, вам следует использовать современный алгоритм хэширования, который не требует от вас создания собственной соли.Использование слабых алгоритмов хеширования представляет опасность как для вас, так и для ваших пользователей.Мой оригинальный ответ был написан восемь лет назад.Времена изменились, и теперь хеширование паролей стало намного проще.

Вы должны всегда использовать встроенные функции для хеширования / проверки паролей.Использование ваших собственных алгоритмов в любой момент вносит огромный ненужный риск.

Для PHP рассмотрите возможность использования password_hash () с алгоритмом PASSWORD_BCRYPT.Не нужно указывать свою собственную соль.

Ниже приведен мой первоначальный ответ для потомков:


Предупреждение : следующая реализация непроизвести непредсказуемую соль, согласно документации для uniqid .

со страницы php sha1 :

$salt = uniqid(mt_rand(), true);

Это выглядитпроще и эффективнее (поскольку каждый из них уникален), чем то, что вы предложили.

18 голосов
/ 04 ноября 2010

Если вы работаете в Linux, /dev/urandom, вероятно, ваш лучший источник случайности.Он поставляется самой ОС, поэтому он гарантированно будет намного надежнее любой встроенной функции PHP.

$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);

Это даст вам 32 байта случайного большого двоичного объекта.Возможно, вы захотите передать это через что-то вроде base64_encode(), чтобы сделать его разборчивым.Не нужно самостоятельно манипулировать символами.

Редактировать 2014: В PHP 5.3 и выше, openssl_random_pseudo_bytes() - это самый простой способ получить набор случайных байтов.В * nix системах он использует /dev/urandom за кулисами.В системах Windows он использует другой алгоритм, встроенный в библиотеку OpenSSL.

Связанный: https://security.stackexchange.com/questions/26206

Связанный: мне использовать urandom или openssl_random_pseudo_bytes?

8 голосов
/ 23 октября 2015

password_hash() доступен в PHP 5.5 и новее.Я удивлен, узнав, что это здесь не упоминается.

С password_hash () нет необходимости генерировать соль, так как соль генерируется автоматически с использованием алгоритма bcrypt - и, следовательно, нет необходимости создаватьнабор символов.

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

Как работает хэш пароля ():

Функция password_hash () выводит уникальный хэш пароля при хранении строки в базе данных - рекомендуется, чтобы столбец допускал до 255 символов.

$password = "goat";
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);

// Output example (store this in the database)
$2y$10$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq  <- This hash changes.
$2y$10$7.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
$2y$10$5m8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y 

Чтобы проверить хешированный пароль, выиспользуйте password_verify():

$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE

Если вы предпочитаете, соль может быть добавлена ​​вручную в качестве опции, например, так:

$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch@7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu  <- This password hash not change.
4 голосов
/ 04 ноября 2010

Я бы посоветовался с другим ответом и использовал бы mt_rand (0, 61) , потому что Mersenne Twister производит лучшую энтропию.

Кроме того, ваша функция на самом деле состоит из двух частей: генерирует случайные $nbLetters цифры и кодирует их в base62. Это станет намного понятнее программисту по техническому обслуживанию (возможно, вам!), Который наткнется на это через несколько лет:

// In a class somewhere
private $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

private function getBase62Char($num) {
    return $chars[$num];
}

public function generateRandomString($nbLetters){
    $randString="";

    for($i=0; $i < $nbLetters; $i++){
        $randChar = getBase62Char(mt_rand(0,61));
        $randString .= $randChar;
    }

    return $randomString;
}
4 голосов
/ 04 ноября 2010

Замените rand(0,61) на mt_rand(0, 61), и с вами все будет в порядке (поскольку mt_rand лучше при создании случайных чисел) ...

Но важнее, чем сила соли, то, как вы ее хешируете. Если у вас есть отличная солевая рутина, но вы выполняете только md5($pass.$salt), вы выбрасываете соль. Я лично рекомендую растянуть хеш ... Например:

function getSaltedHash($password, $salt) {
    $hash = $password . $salt;
    for ($i = 0; $i < 50; $i++) {
        $hash = hash('sha512', $password . $hash . $salt);
    }
    return $hash;
}

Для получения дополнительной информации о растягивании хеша, посмотрите этот SO ответ ...

2 голосов
/ 30 декабря 2014

Вот гораздо лучший способ, если у вас есть окна и вы не можете сделать /dev/random.

//Key generator
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
//The variable $secure is given by openssl_random_ps... and it will give a true or false if its tru then it means that the salt is secure for cryptologic.
while(!$secure){
    $salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
}
2 голосов
/ 09 апреля 2014

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

<?php
function generateRandomString($length = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $randomString;
}

function get_true_random_number($min = 1, $max = 100) {
    $max = ((int) $max >= 1) ? (int) $max : 100;
    $min = ((int) $min < $max) ? (int) $min : 1;
    $options = array(
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER => false,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_ENCODING => '',
        CURLOPT_USERAGENT => 'PHP',
        CURLOPT_AUTOREFERER => true,
        CURLOPT_CONNECTTIMEOUT => 120,
        CURLOPT_TIMEOUT => 120,
        CURLOPT_MAXREDIRS => 10,
    );

    $ch = curl_init('http://www.random.org/integers/?num=1&min='
        . $min . '&max=' . $max . '&col=1&base=10&format=plain&rnd=new');
    curl_setopt_array($ch, $options);
    $content = curl_exec($ch);
    curl_close($ch);

    if(is_numeric($content)) {
        return trim($content);
    } else {
        return rand(-10,127);
    }
}

function generateSalt() {
    $string = generateRandomString(10);
    $int = get_true_random_number(-2,123);
    $shuffled_mixture = str_shuffle(Time().$int.$string);
    return $salt = md5($shuffled_mixture);
}

echo generateSalt();
?>

Атмосферный шум обеспечивается random.orgЯ также видел действительно случайную генерацию из изображений лавовых ламп, которые интерпретируются через оттенок и местоположение.(Оттенок это местоположение)

1 голос
/ 27 февраля 2014

Я использую это:

$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
1 голос
/ 30 мая 2013

Довольно простая техника:

$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2);  // or whatever sized salt is wanted

В отличие от uniqid () он генерирует случайный результат.

1 голос
/ 04 ноября 2010

Я думаю, что, например, очень хорошая соль - это имя пользователя (если вы говорите о хешировании pw, а имя пользователя не меняется)не нужно хранить дополнительные данные.

...