Я создаю систему входа в систему, и я хочу быть уверенным, что пишу код для генерации и хранения паролей в db. $options['passwd']
– это строка, выбранная пользователем как пароль.
Это мой код для генерации хэша:
public function createPasswd($options) { $hash_seed = 'm9238asdasdasdad31d2131231231230132k32ka¡2da12sm2382329'; $password = $options['passwd']; $date = new DateTime(); $timestamp = $date->getTimestamp(); $rand_number = rand($timestamp,$timestamp + pow(91239193912939123912,3)); $rand = pow($rand_number, 12); $salt = str_shuffle($rand.$hash_seed); $hash = crypt($password, $salt); return $hash; }//End class createPasswd
Я просто храню хэш в базе данных, а затем сравниваю его с паролем пользователя следующим образом:
if ($hash == crypt($password, $hash)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; }
Является ли это достаточно сильным? Я пропустил какую-то большую проблему ?.
Более продолжительная соль не означает лучшей защиты. Вы не используете функцию склепа правильно. $ salt аргумент не должен быть простой случайной строкой.
Рассмотрим этот пример:
echo crypt('password one', 'salt lorem ipsum dolor sit amet'); echo crypt('password two', 'salt');
Оба возвращают ту же строку! (Sa3tHJ3 / KuYvI)
Проверьте http://php.net/crypt для получения дополнительной информации о том, как правильно использовать $ salt.
Также намного лучше (безопаснее?) Сохранить уникальную часть кода hash_seed, а затем хранить в базе данных только ша-хэш (или другое слово) строки, объединяющей пароль и ваш hash_seed.
Правильная реализация будет:
define('SECRET_KEY', 'm9238asdasdasdad31d2131231231230132k32ka¡2da12sm2382329'); // longer is better public function createPasswd($options) { return hash('sha256', SECRET_KEY . $options['passwd']); }
Чтобы проверить пароль:
if ($stored_hash == hash('sha256', SECRET_KEY . $password) { echo 'Password is valid!'; } else { echo 'Invalid password.'; }
sha256 можно заменить любыми доступными алгоритмами в вашей системе. Получить полный список:
var_dump(hash_algos());
Ладно, давайте посмотрим:
Вы генерируете число между текущей меткой времени (≥ 1393631056) и чем-то около 7.59E + 59:
$rand_number = rand($timestamp,$timestamp + pow(91239193912939123912,3)); $rand = pow($rand_number, 12);
Эти значения и расчеты кажутся просто произвольными. Честно говоря, если случайное значение уже велико, возможно, что pow($rand_number, 12)
возвращает INF
.
Затем вы помещаете случайное число и фиксированное семя, перетасовываете его и используете его как соль с crypt
для хеширования пароля:
$salt = str_shuffle($rand.$hash_seed); $hash = crypt($password, $salt);
Случайное число с плавающей точкой плюс фиксированная соль дают только 16 (в случае INF
) и 20 разных символов. Однако, глядя ближе к crypt
видно, что если вы не укажете алгоритм, crypt
будет использовать CRYPT_STD_DES
:
Стандартный хэш на основе DES с солью из двух символов из алфавита «
./0-9A-Za-z
». Использование недопустимых символов в соли приведет к сбоюcrypt()
.
Теперь в этих предложениях есть два аспекта, которые должны сделать вас подозрительными:
./0-9A-Za-z
, иначе crypt
не удастся. Таким образом, ваша соль не только содержит символы, отличные от ./0-9A-Za-z
, но с наихудшим набором символов 16 символов имеется только 16 ^ 2 = 256 возможных солей.
Итак, что нужно делать? Просто не пытайтесь изобретать колесо, но используйте существующие и проверенные решения.
Является ли это достаточно сильным? Я пропустил какую-то большую проблему?
Современный PHP фактически обеспечивает исключительно хорошее хеширование паролей, встроенное в – BCrypt, один из трех (SCrypt, BCrypt, PBKDF2), последовательно рекомендуемых хэш-функций пароля (на начало 2014 года). Еще лучше, он обрабатывает соление для вас (соль должна быть просто случайной и достаточно длинной).
Если вы используете PHP 5.5 или новее, прочитайте « Safe Password Hashing» на PHP.net – это FAQ для хранения паролей в PHP. Если вы используете PHP 5.3.7, но еще не 5.5, вы можете использовать библиотеку password_compat для использования этих функций.
Эти функции используют BCrypt и обрабатывают соль для вас, так что это легко!
В частности, вы можете хэш-пароль с достаточно высокой стоимостью (выберите стоимость, которая занимает достаточно много времени, что при ожидаемой максимальной нагрузке ваш сайт будет не совсем привязан к процессору) – например, в password_hash Пример 2 :
<?php /** * In this case, we want to increase the default cost for BCRYPT to 12. * Note that we also switched to BCRYPT, which will always be 60 characters. */ $options = [ 'cost' => 12, ]; echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n"; ?>
Затем вы сохраняете возвращаемую строку.
Чтобы проверить, верните строку, которую она вернула, где бы вы ее не хранили (т. Е. Ваша база данных), и сравните ее с примером password_verify :
<?php // See the password_hash() example to see where this came from. $hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq'; if (password_verify('rasmuslerdorf', $hash)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; } ?>
Как всегда, если вам нужны детали, пожалуйста, прочитайте как безопасно использовать хэш-пароли? – но функции пароля PHP 5.5 используют Bcrypt, поэтому вы можете использовать достаточно высокую стоимость.
Обратите внимание, что со временем вы покупаете лучшее оборудование, вы должны увеличить стоимость, чтобы соответствовать. Вы можете прозрачно сделать это, проверив пароль по старой цене, проверив старую стоимость и, если она будет найдена, переименуйте ее с новой стоимостью как часть процесса входа в систему, чтобы вы могли повысить безопасность сохраненных паролей, не беспокоясь ваших пользователей. Конечно, пользователи, которые никогда не входят в систему, не получают преимуществ от этого.
Для старого примера pre-5.3.7 crypt () см. Главный ответ на вопрос: Как вы используете bcrypt для хэширования паролей в PHP? , который имеет функцию getSalt:
private function getSalt() { $salt = sprintf('$2a$%02d$', $this->rounds); $bytes = $this->getRandomBytes(16); $salt .= $this->encodeBytes($bytes); return $salt; }
Я уверен, что ты переусердствовал.
Это достаточно, если вы будете использовать что-то вроде
$salt = get_random_salt(); // will return any random string $hash = md5(md5($salt).md5($user_password)); // or any combinations of hashing and concatenations. It's your secure alorithm. Or use other more resistant hash algorithm.
Чем вам нужно сэкономить $hash
и $salt
в БД и проверить таким же образом:
$hash = md5(md5($salt_from_db).md5($user_password)); if ($hash == $hash_from_db) { // success } else { // fail }
Также в вашем примере вы делаете каждый раз операцию pow(91239193912939123912,3)
но каждый раз он будет возвращать одинаковый результат. Таким образом, это то же самое, если вы добавите только предварительно рассчитанное значение.
Я имею в виду, что не обязательно иметь этот сложный алгоритм соли. Вы можете просто использовать mt_rand()
для этой цели.
Также см. Здесь: Безопасный хеш и соль для паролей PHP
Серфинг в Интернете я нашел лучшее решение, которое использует blowfish. При таком подходе я понимаю, что мне нужно хранить хэш только в базе данных, а не в соли. Очевидно, что чем больше раундов, тем сильнее пароль и более медленное поколение.
public function better_crypt($input, $rounds = 8) { $salt = ""; $salt_chars = array_merge(range('A','Z'), range('a','z'), range(0,9)); for($i=0; $i < 22; $i++) { $salt .= $salt_chars[array_rand($salt_chars)]; } return crypt($input, sprintf('$2a$%02d$', $rounds) . $salt); }//End function if(crypt($pass_string, $passwd) == $passwd) { echo 'password is correct'; } }
Используйте password_hash (), который по умолчанию использует bcrypt под капотом: http://ie2.php.net/password_hash
Если вы не можете использовать его из своей версии PHP, есть: https://github.com/ircmaxell/password_compat
Если вы используете что-нибудь еще, пытаясь написать его самостоятельно, остановитесь сейчас и, пожалуйста, не слушайте идиотов, предлагающих любой другой вариант. Особенно, когда они предлагают MD5 …