Как безопасно хранить пароли пользователей?

Насколько безопаснее это, чем обычный MD5? Я только начал изучать безопасность паролей. Я новичок в PHP.

$salt = 'csdnfgksdgojnmfnb';

$password = md5($salt.$_POST['password']);
$result = mysql_query("SELECT id FROM users
                       WHERE username = '".mysql_real_escape_string($_POST['username'])."'
                       AND password = '$password'");

if (mysql_num_rows($result) < 1) {
    /* Access denied */
    echo "The username or password you entered is incorrect.";
} 
else {
    $_SESSION['id'] = mysql_result($result, 0, 'id');
    #header("Location: ./");
    echo "Hello $_SESSION[id]!";
}

Ответ 1

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

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


Новый API паролей PHP (5.5.0 +)

Если вы используете PHP версии 5.5.0 или новее, вы можете использовать новый упрощенный хэширующий API-интерфейс

Пример кода с использованием API паролей PHP:

<?php
// $hash is what you would store in your database
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT, ['cost' => 12]);

// $hash would be the $hash (above) stored in your database for this user
$checked = password_verify($_POST['password'], $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}

(Если вы все еще используете устаревшие версии 5.3.7 или новее, вы можете установить ircmaxell/password_compat для доступа к встроенным функциям )


Улучшение на соленых хешах: добавить перец

Если вам нужна дополнительная безопасность, пользователи безопасности теперь (2017) рекомендуют добавлять "pepper к (автоматически) соленым хэшам паролей.

Существует простой класс, который надежно реализует этот шаблон, я рекомендую: Netsilik/PepperedPasswords (github).
Он поставляется с лицензией MIT, поэтому вы можете использовать его, как хотите, даже в проприетарных проектах.

Пример кода с использованием Netsilik/PepperedPasswords:

<?php
use Netsilik/Lib/PepperedPasswords;

// Some long, random, binary string, encoded as hexadecimal; stored in your configuration (NOT in your Database, as that would defeat the entire purpose of the pepper).
$config['pepper'] = hex2bin('012345679ABCDEF012345679ABCDEF012345679ABCDEF012345679ABCDEF');

$hasher = new PepperedPasswords($config['pepper']);

// $hash is what you would store in your database
$hash = $hasher->hash($_POST['password']);

// $hash would be the $hash (above) stored in your database for this user
$checked = $hasher->verify($_POST['password'], $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}


Стандартная библиотека OLD

Обратите внимание: вам больше не понадобится! Это только здесь для исторических целей.

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

Пример кода с использованием phpass (v0.2):

<?php
require('PasswordHash.php');

$pwdHasher = new PasswordHash(8, FALSE);

// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );

// $hash would be the $hash (above) stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}

PHPass реализован в некоторых довольно известных проектах:

  • phpBB3
  • WordPress 2.5+, а также bbPress
  • версия Drupal 7 (модуль доступен для Drupal 5 и 6)
  • другие

Хорошо, что вам не нужно беспокоиться о деталях, эти детали были запрограммированы людьми с опытом и просмотрены многими людьми в Интернете.

Для получения дополнительной информации о схемах хранения паролей прочитайте сообщение Jeff: Вы Вероятно, хранение паролей неверно

Что бы вы ни делали, если вы идете на "Я сделаю это сам, спасибо", больше не используйте MD5 или SHA1. Они являются хорошим алгоритмом хеширования, но считаются прерванными в целях безопасности.

В настоящее время, используя crypt, лучше всего использовать CRYPT_BLOWFISH.
CRYPT_BLOWFISH в PHP - это реализация хэша Bcrypt. Bcrypt основан на блочном шифре Blowfish, используя его дорогостоящую настройку ключа, чтобы замедлить алгоритм.

Ответ 2

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

Ответ 3

Лучшим способом для каждого пользователя будет уникальная соль.

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

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

Ответ 4

С PHP 5.5 (то, что я описываю, доступно для более ранних версий, см. ниже) за углом, я хотел бы предложить использовать его новое встроенное решение: password_hash() и password_verify(). Он предоставляет несколько опций для достижения требуемого уровня безопасности паролей (например, путем указания параметра "стоимость" через массив $options)

<?php
var_dump(password_hash("my-secret-password", PASSWORD_DEFAULT));

$options = array(
    'cost' => 7, // this is the number of rounds for bcrypt
    // 'salt' => 'TphfsM82o1uEKlfP9vf1f', // you could specify a salt but it is not recommended
);
var_dump(password_hash("my-secret-password", PASSWORD_BCRYPT, $options));
?>

вернет

string(60) "$2y$10$w2LxXdIcqJpD6idFTNn.eeZbKesdu5y41ksL22iI8C4/6EweI7OK."
string(60) "$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d."

Как вы могли видеть, строка содержит соль, а также стоимость, указанную в параметрах. Он также содержит используемый алгоритм.

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

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

Проверка работает следующим образом:

var_dump(password_verify("my-secret-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("wrong-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));

var_dump(password_verify("my-secret-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
var_dump(password_verify("wrong-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));

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

Существует небольшая библиотека (один файл PHP), которая даст вам PHP 5.5 password_hash в PHP 5.3.7+: https://github.com/ircmaxell/password_compat

Ответ 5

Это хорошо со мной. Г-н Атвуд писал о о силе MD5 против радужных таблиц, и в основном с длинной солью, как будто вы сидите довольно (хотя некоторые случайные знаки пунктуации /numbers, он может улучшить его).

Вы также можете посмотреть SHA-1, который, кажется, становится более популярным в наши дни.

Ответ 6

Я хочу добавить:

  • Не ограничивайте пароли пользователей по длине

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

  • Не отправлять пароли пользователей по электронной почте

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

  • Обновление хэшей паролей пользователей

Хэш пароля может быть устаревшим (параметры алгоритма могут быть обновлены). Используя функцию password_needs_rehash(), вы можете проверить ее.