我正在将一个较旧的 ruby-on-rails 站点迁移到 node/express/react/redux,并且在验证用户现有密码时遇到了麻烦。该站点目前正在使用 AuthLogic(scrypt) 进行身份验证/密码散列,一些较旧的帐户密码仍然使用 sha512 AuthLogic 算法进行散列。
我一辈子都想不出如何在 Node.js 中复制 authLogic 的 scrypt 算法。我已经查看了 AuthLogic 的 ruby 源代码和底层的 scrypt 包,这给了我一些线索。
我正在使用最新版本的 npm scrypt 包(我已经尝试了其他一些没有明显差异的包)。
存储的哈希如下所示:
400$8$1d$3fbb0d3688d9da6d$5dd919ace6bdf946d48946e9dd61f0afc5116986433633e24e58809c12b5ce9a
数据库还存储了一个唯一的 salt 参数:fbMQa7EhFp5tdOhNsT
根据 scrypt gem 源,看起来 $ 分隔的段是成本因素和盐:
n, r, p = args[0].split('$').map{ |x| x.to_i(16) }
来自 Scrypt gem scrypt.rb源。
根据代码,看起来前三个 $ 分隔位是 scrypt 的“成本”因素,最后一个是盐。我不知道为什么这种盐与存储在数据库中的盐不同,或者我应该如何插入它们。这让我尝试:
const n = parseInt("400", 16)
const r = parseInt("8", 16);
const p = parseInt("1d", 16)
const result = scrypt.hashSync("[my password]",{"N":n,"r":r,"p":p}, 32, 3fbb0d3688d9da6d);
结果是与数据库存储不同的哈希,但至少是正确的长度。
80e302f9f8942ec9d81fe217c03730b5b8256b22cd91ad2dd2a448ec588ec390
所以我尝试了一个比较:
const comparision = scrypt.verifyKdfSync(
"5dd919ace6bdf946d48946e9dd61f0afc5116986433633e24e58809c12b5ce9a",
"[mypassword]");
但这失败了,告诉我数据不是 scrypt 散列数据。我尝试在散列中添加和不使用 $ 分隔符的各种成本因子和盐,但无济于事。我尝试将散列转换为缓冲区对象,也无济于事。尝试使用上述选项的 kdfSync 也失败了(错误计算派生密钥)。在线工具也不会将存储的哈希识别为 scrypt 哈希。
帮助我红宝石魔术师(或其他任何有密码学的人),你是我唯一的希望。