给定相同的输入,为什么节点scrypt哈希值相同?

时间:2018-12-15 18:49:45

标签: node.js cryptography 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设计。话虽这么说,任何可接受的答案都应包括为什么此方法是安全的,或足以切换的安全性(授予“足够安全”永远不会足够安全)。我将安全作为我的专业,但是现在我是一名Web开发人员,尽管核心概念几乎保持不变,但安全一直在变化。

1 个答案:

答案 0 :(得分:3)

您似乎对密码哈希有一些基本的误解。首先,就像任何哈希函数一样,密码哈希函数在数学意义上也是 function 。即它只是一个映射,它从其范围向其输入域的每个元素分配一个固定值。

将密码哈希与常规哈希区分开的原因有两点:首先,密码哈希被设计为较慢和/或在评估时使用大量内存。 (这与我们在这里的讨论无关。)其次,他们输入了第二个输入,即盐。

对于密码哈希函数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时,库将选择一个salt s,计算h并输出h'=(h,s)作为“哈希值”。要检查密码m'是否正确,请提交h',m',然后库将提取出盐,重新计算哈希值并进行比较。

您正在查看的库希望用户选择盐。即,每次在密码数据库中创建新条目时,您都必须选择一个新的盐,计算h = H(m,s)并同时存储两个(h,s)。由于库在这种情况下不会尝试“隐藏”您的任何内容,因此您需要进行比较。