Почему хэши scrypt узла одинаковы при одинаковых входных данных?
Я пытался найти функцию сравнения или проверки для встроенного криптомодуля узла, специально для scrypt, так как большинство модулей хеширования паролей, которые я использовал, имеют такую функцию. Затем я обнаружил, почему это было невыполнимой задачей: все хэши, созданные с помощью этих алгоритмов с использованием одинаковых параметров, генерируют одну и ту же строку (технически буфер). Это касается многих crypto
Хэширующие функции, включая его pbkdf2
реализация.
Почему это безопасно? Разве не весь (современный) смысл функции хеширования пароля / сообщения заключается в том, что вы не можете сгенерировать тот же пароль / сообщение снова, используя тот же ввод? Вот как работают различные модули bcrypt, а также оригинальная версия scrypt, из которой получена встроенная версия, о которой я спрашиваю.
Например:
let scryptHash1;
let scryptHash2;
let scryptHash3;
let pbkdfHash1;
let pbkdfHash2;
let pbkdfHash3;
const key1 = 'my secret key';
const key2 = 'my other secret key';
const salt = 'my salt';
crypto.scrypt(key1, salt, 16, hash => scryptHash1 = hash);
crypto.scrypt(key1, salt, 16, hash => scryptHash2 = hash);
crypto.scrypt(key2, salt, 16, hash => scryptHash3 = hash);
scryptHash1.toString() === scryptHash2.toString(); // true
scryptHash1.toString() === scryptHash3.toString(); // false
crypto.pbkdf2(key1, salt, 16, 16, 'sha256', hash => pbkdfHash1 = hash);
crypto.pbkdf2(key1, salt, 16, 16, 'sha256', hash => pbkdfHash2 = hash);
crypto.pbkdf2(key2, salt, 16, 16, 'sha256', hash => pbkdfHash3 = hash);
pbkdfHash1.toString() === pbkdfHash2.toString(); // true
pbkdfHash1.toString() === pbkdfHash3.toString(); // false
Я изначально задавал этот вопрос на Cryptography
так как меня больше заботит безопасность, чем что-либо еще, так как я хочу отойти от bcrypt
в scrypt
, Однако, как отмечали несколько человек, и, как я и опасался, вопрос скорее в разработке API. При этом любой принятый ответ должен включать, почему этот метод является безопасным или достаточно безопасным для переключения (предоставление этого "достаточно безопасного" никогда не является достаточно безопасным). Я выбрал безопасность в качестве основного специалиста, но теперь я веб-разработчик, и безопасность постоянно меняется, хотя основные концепции остаются в основном неизменными.
1 ответ
Похоже, у вас есть фундаментальное недопонимание по поводу хэширования пароля Прежде всего, как и любая хеш-функция, хеш-функция пароля также является функцией в математическом смысле. Т.е. это просто отображение, которое присваивает фиксированное значение из своего диапазона каждому элементу входной области.
То, что отличает хэши паролей от обычных хэшей, это две вещи: во-первых, они разработаны так, чтобы быть медленными и / или использовать большие объемы памяти при оценке. (Это не имеет значения для нашего обсуждения здесь.) И во-вторых, они берут второй вклад, соль.
Для функции хеширования паролей H вы хотите, чтобы для любого фиксированного пароля m и любых двух солей s≠ s'он не только содержал H(m,s)≠ H(m,s'), но также давал значения хеш-функции и соли Вы не должны быть в состоянии обнаружить, что они являются значениями хеш-функции одного и того же m.
То, что вы, кажется, смущены, это разные варианты дизайна API. В частности, кто может выбрать соль. Каждый раз, когда хэшируется новый пароль m (например, для ввода в базу данных), должна быть выбрана свежая равномерно случайная соль s, а затем вычисляется значение хеша h:=H(m,s) и сохраняются оба значения h и s. в базе данных. Всякий раз, когда кто-то, претендующий на то, чтобы быть тем же пользователем, отправляет пароль m'для аутентификации, происходит то, что (h,s) извлекается и проверяется, является ли h=H(m',s).
Теперь вопрос в том, кто выбирает соль. Похоже, что знакомые вам API не доверяют пользователю. Поэтому, когда вы вызываете хэш-пароль m, библиотека выберет соль s, вычислит h и выведет h'=(h,s) в качестве "значения хеша". Чтобы проверить правильность пароля m', вы затем отправляете h',m', и библиотека извлечет соль, пересчитает хеш и сравнит.
Библиотека, которую вы сейчас просматриваете, ожидает, что пользователь выберет соль. То есть, каждый раз, когда вы создаете новую запись в базе данных паролей, вы должны выбрать новую соль, вычислить h=H(m,s) и сохранить оба (h,s). Поскольку библиотека в этом случае не пытается "что-то скрыть" от вас, вам нужно позаботиться о сравнении.