为什么在给定相同输入的情况下节点 scrypt 哈希值相同?
Why Are node scrypt Hashes the Same Given the Same Inputs?
我试图为节点的内置加密模块寻找比较或验证功能,特别是针对 scrypt,因为我使用的大多数密码散列模块都有这样的功能。然后,我发现了为什么这是一项不可能完成的任务:使用相同参数使用这些算法生成的所有哈希值都生成相同的字符串(技术上是缓冲区)。许多 crypto
的散列函数都是这种情况,包括其 pbkdf2
实现。
为什么这样安全? password/message 散列函数的全部(现代)点不是不能使用相同的输入再次生成相同的 password/message 吗?这就是各种 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 设计。话虽这么说,任何接受的答案都应该包括为什么这种方法是安全的,或者足够安全以进行切换(假设 "safe enough" 永远不够安全)。我把安全作为我的专业,但我现在是一名网络开发人员,安全一直在变化,尽管核心概念大部分保持不变。
您似乎对密码散列有一些根本性的误解。首先,就像任何哈希函数一样,密码哈希函数在数学意义上也是一个 函数 。 IE。它只是一个映射,将其范围内的固定值分配给其输入域的每个元素。
密码散列与常规散列的区别在于两点:首先,它们的设计速度较慢 and/or 在评估时使用大量内存。 (这与我们在这里的讨论无关。)其次他们接受第二个输入,即盐。
对于密码哈希函数 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).
现在的问题是谁选择盐。您熟悉的 APIs 似乎不相信用户会这样做。因此,当您调用散列密码 m 时,库将选择盐 s,计算 h 并将 h'=(h,s) 输出为 "hash value"。要检查密码 m' 是否正确,然后提交 h',m' 库将提取盐,重新计算哈希值并进行比较。
您现在查看的库希望用户选择盐。也就是说,每次你在密码数据库中创建一个新条目时 你 必须选择一个新的盐,计算 h=H(m,s) 并存储两者 (h,s)。由于本例中的图书馆不会尝试 "hide" 您的任何内容,因此您需要进行比较。
我试图为节点的内置加密模块寻找比较或验证功能,特别是针对 scrypt,因为我使用的大多数密码散列模块都有这样的功能。然后,我发现了为什么这是一项不可能完成的任务:使用相同参数使用这些算法生成的所有哈希值都生成相同的字符串(技术上是缓冲区)。许多 crypto
的散列函数都是这种情况,包括其 pbkdf2
实现。
为什么这样安全? password/message 散列函数的全部(现代)点不是不能使用相同的输入再次生成相同的 password/message 吗?这就是各种 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 设计。话虽这么说,任何接受的答案都应该包括为什么这种方法是安全的,或者足够安全以进行切换(假设 "safe enough" 永远不够安全)。我把安全作为我的专业,但我现在是一名网络开发人员,安全一直在变化,尽管核心概念大部分保持不变。
您似乎对密码散列有一些根本性的误解。首先,就像任何哈希函数一样,密码哈希函数在数学意义上也是一个 函数 。 IE。它只是一个映射,将其范围内的固定值分配给其输入域的每个元素。
密码散列与常规散列的区别在于两点:首先,它们的设计速度较慢 and/or 在评估时使用大量内存。 (这与我们在这里的讨论无关。)其次他们接受第二个输入,即盐。
对于密码哈希函数 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).
现在的问题是谁选择盐。您熟悉的 APIs 似乎不相信用户会这样做。因此,当您调用散列密码 m 时,库将选择盐 s,计算 h 并将 h'=(h,s) 输出为 "hash value"。要检查密码 m' 是否正确,然后提交 h',m' 库将提取盐,重新计算哈希值并进行比较。
您现在查看的库希望用户选择盐。也就是说,每次你在密码数据库中创建一个新条目时 你 必须选择一个新的盐,计算 h=H(m,s) 并存储两者 (h,s)。由于本例中的图书馆不会尝试 "hide" 您的任何内容,因此您需要进行比较。