Как может быть bcrypt иметь встроенные соли?

Coda Hale article "Как безопасно хранить пароль" утверждает, что:

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

Он цитирует эту статью, в которой говорится, что в реализации OpenBSD bcrypt:

OpenBSD генерирует 128-битную соль bcrypt из arcfour (arc4random (3)) ключевой поток, засеянный случайными данными, ядро собирает из таймингов устройства.

Я не понимаю, как это может работать. В моей концепции соли:

  • Для каждого сохраненного пароля он должен быть различным, поэтому для каждого необходимо создать отдельную таблицу радуги.
  • Его нужно хранить где-то так, чтобы он повторялся: когда пользователь пытается войти в систему, мы предпринимаем попытку пароля, повторяем ту же самую соль-и-хэш-процедуру, которую мы сделали, когда мы изначально сохранили их пароль, и сравните

Когда я использую Devise (менеджер входа Rails) с bcrypt, в базе данных нет столбца солей, поэтому я запутался. Если соль случайна и не хранится нигде, как мы можем достоверно повторить процесс хеширования?

Короче говоря, как может bcrypt иметь встроенные соли?

Ответ 1

Это bcrypt:

Генерация случайной соли. Коэффициент стоимости был предварительно настроен. Собери пароль.

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

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

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


Сохраненный в базе данных, хеш bcrypt может выглядеть примерно так:

$ 2a $ 10 $ vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

На самом деле это три поля, разделенные символом "$":

  • 2a идентифицирует версию алгоритма bcrypt, которая использовалась.
  • 10 - фактор стоимости; 2 10 итераций функции получения ключа (кстати, этого недостаточно. Я бы рекомендовал стоимость 12 или более).
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa - это солевой и зашифрованный текст, объединенный и закодированный в модифицированной Base-64. Первые 22 символа декодируются до 16-байтового значения для соли. Остальные символы - это зашифрованный текст, который нужно сравнить для аутентификации.

Этот пример взят из документации по реализации Coda Hale ruby.

Ответ 2

Я считаю, что фраза должна была быть сформулирована следующим образом:

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

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

Ответ 3

Это из документации по интерфейсу PasswordEncoder от Spring Security,

 * @param rawPassword the raw password to encode and match
 * @param encodedPassword the encoded password from storage to compare with
 * @return true if the raw password, after encoding, matches the encoded password from
 * storage
 */
boolean matches(CharSequence rawPassword, String encodedPassword);

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