Как мне добавить соль в мой логин для паролей? - PullRequest
54 голосов
/ 03 февраля 2010

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

Я также нашел другой пост здесь на SO, где другой разработчик сказал:

"Убедитесь, что ваша соль и алгоритм хранится отдельно от базы данных "

Я бы хотел хранить соль в базе данных. Это действительно проблема, если я это сделаю?

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


EDIT: Я хочу поблагодарить всех за их ответы и идеи. Несмотря на то, что сейчас я могу быть более запутанным, это, безусловно, стало для меня опытом обучения. Еще раз спасибо, ребята.

Ответы [ 8 ]

31 голосов
/ 03 февраля 2010

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

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

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

Редактировать: Мои конкретные рекомендации:

  • Генерируйте длинную псевдослучайную соль для каждого пользователя и сохраняйте ее в БД
  • В идеале используйте хеш на основе bcrypt
  • , в идеале не используйте его самостоятельно, используйте существующая библиотека вместо
18 голосов
/ 03 февраля 2010

Хеш-функция всегда возвращает одно и то же значение для одной и той же входной строки. Допустим, мой пользователь (Алиса) имеет пароль secret. Хеширование secret с использованием md5() приводит к следующему хешу

5ebe2294ecd0e0f08eab7690d2a6ee69

Используя словарь (список общих слов и пароль) или один из различных сайтов, предлагающих вам эту услугу, злоумышленник (Мэллори) может легко узнать, что пароль является секретным, когда он видит в своем словаре, что 5ebe2294ecd0e0f08eab7690d2a6ee69 = secret .

Процесс посола перед хэшированием затрудняет использование атаки по словарю, не зная вашей соли. Учтите следующее:

<?php
$salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!';
$hash = md5($salt . 'secret');

Полученный хеш теперь равен b58ad809eece17322de5024d79299f8a, но пароль Алисы по-прежнему secret. Теперь, если Мэллори достанет соленый хеш, скорее всего, она не найдет ответ в своем словаре. Если она это сделает, словарь даст ей неправильный ответ.

Никогда не храните статическую соль в вашей базе данных. Желательно хранить его с конфигурацией вашего приложения (которая, кстати, не должна быть доступна в Интернете).

Если вы собираетесь использовать динамическую соль, вам нужно будет использовать базу данных. Используйте ненулевой столбец существующих действительных данных для построения вашей соли (зашифрованная строка имени пользователя, основанная на секретном ключе шифрования, обычно криптографически безопасна). Не используйте отдельный столбец для соли. Если вы не можете использовать существующий столбец, включите вашу соль в тот же столбец, что и ваш хэш. Например, используйте первые 32 символа для вашей 128-битной соли, а затем последние 40 для 160-битного хеша. Следующая функция сгенерирует такой хеш:

function seeded_sha1($string, $seed_bits) {
    if(($seed_bits % 8) != 0) {
        throw new Exception('bits must be divisible by 8');
    }

    $salt = '';
    for($i = 0; $i < $seed_bits; $i+=8) {
        $salt .= pack('c', mt_rand());
    }

    $hexsalt = unpack('h*hex', $salt);

    return $hexsalt['hex'] . sha1($salt . $string);
}

function compare_seeded_sha1($plain, $hash) {
    $sha1 = substr($hash, -40);
    $salt = pack('h*', substr($hash, 0, -40));

    $plain_hash = sha1($salt . $plain);
    return ($plain_hash == $sha1);
}

Если злоумышленник войдет в вашу базу данных с помощью SQL-инъекции, то по крайней мере хеш-коды, которые он / она получит, будут бесполезны, поскольку у него / нее не будет доступа к конфигурации вашего приложения. Если ваш сервер рутирован, он в значительной степени закончен, независимо от того, что вы делаете.

Примечание: Возможны другие типы атак на md5(), поэтому вы используете более безопасный алгоритм хеширования, например, sha1(). Или, что еще лучше, используйте Portable PHP хэширование паролей , разработанное с учетом требований безопасности и обратно совместимое практически с любой версией 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 $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}
15 голосов
/ 03 февраля 2010

Забудьте об использовании солей (частично по той причине, которую вы упомянули), используйте вместо этого bcrypt:

Хорошее объяснение см .: http://codahale.com/how-to-safely-store-a-password/

7 голосов
/ 03 февраля 2010

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

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

В качестве примера наличия уникальной соли для каждого пользователя и ее хранения вместе с хэшем пароля, я укажу / etc / shadow в вашей типичной системе Linux.

root@linux:/root# cat /etc/shadow | grep root
root:$1$oL5TTZxL$RhfGUZSbFwQN6jnX5D.Ck/:12139:0:99999:7:::

Здесь oL5TTZxL - это соль, а RhfGUZSbFwQN6jnX5D.Ck / - это хеш. В этом случае простой текстовый пароль - root , а алгоритм хеширования, используемый моей системой, - это алгоритм BSD-пароля на основе MD5. (более новые системы, чем у меня, имеют лучшие алгоритмы хэширования)

5 голосов
/ 03 февраля 2010

Вы не получите пароль для сравнения. Вы шифруете пароль при попытке входа в систему и сравниваете сохраненное значение с новым зашифрованным значением.

2 голосов
/ 03 февраля 2010

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

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

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

3) Вы можете иметь отдельную соль для каждого пользователя: база данных будет использоваться для хранения соли, а хеш получениз комбинации пароль / соль.Это предотвратит атаку радуги, но атаки с использованием грубой силы все еще будут возможны.

4) Наконец, вы можете сохранить свою хэш-функцию в секрете, используя аппаратное хеш-решение с ограниченной скоростью.

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

Некоторые другие примечания:

a) Вы можете использовать bcrypt накомбинация пароль / соль, чтобы замедлить атаку грубой силы атакующего.Но так как мы предполагаем администраторов, они могут быть терпеливыми.

b) Хранение соли отдельно от хэша пароля не является эффективной защитой, в конце концов, мы предполагаем администраторов.

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

2 голосов
/ 03 февраля 2010

Как вы упомянули, алгоритмы хеширования работают только односторонне (или только если они достаточно сильны :-D)

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

Это очень безопасный способ хранения паролей, так как даже если база данных взломана, хакерам / взломщикам все равно нужно получить статический хеш-код строки и угадать, как вы применили все засоления.

Например, допустим, у вас есть таблица users со следующими столбцами:

id
username
password
created_at

столбцы id и create_at после однократного заполнения никогда не должны быть изменены ..

поэтому, когда вы хэшируете пароль пользователя, вы можете сделать так:

<?php
    $staticSalt = '!241@kadl;ap][';
    $userPass = 'my new pass';
    // assuming $user variable is already populated with DB data
    // we will generate new hash from columns and static salt:
    $genPass = sha1($user['id'] . $userPass . $user['created_at'] . $staticSalt);
?>

Надеюсь, это поможет :) Ура

1 голос
/ 03 февраля 2010

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

Например, если хакер получает доступ к вашей базе данных, а пароли не засолены, он может найти хеши в своей базе данных хэшей (см. http://en.wikipedia.org/wiki/Rainbow_table), чтобы получить оригинальные пароли.

...