是否有任何通常被认为值得信赖的 SHA-256 javascript 实现?

IT技术 hash javascript sha256 sha2
2021-02-10 08:38:46

我正在为论坛编写登录名,并且需要在将密码客户端发送到服务器之前在 javascript 中散列密码客户端。我无法确定我真正可以信任的 SHA-256 实现。我期待有某种每个人都使用的权威脚本,但我发现大量不同的项目都有自己的实现。

我意识到使用其他人的加密货币总是一种信念的飞跃,除非你有资格自己审查它,并且“值得信赖”没有通用的定义,但这似乎是一些普遍且重要的事情,应该有某种形式使用什么的共识。我只是天真吗?

编辑,因为它在评论中出现了很多:是的,我们在服务器端再次进行了更严格的散列。客户端散列不是我们保存在数据库中的最终结果。客户端散列是因为人类客户端请求它。他们没有给出具体的原因,可能他们只是喜欢矫枉过正。

6个回答

过时:许多现代浏览器现在都对加密操作提供一流的支持。请参阅下面的Vitaly Zdanevich 的回答


斯坦福JS加密库包含SHA-256的实现。虽然 JS 中的加密并没有像其他实现平台那样经过严格审查,但该平台至少部分由Dan Boneh开发,并在一定程度上由Dan Boneh赞助,Dan Boneh是密码学领域知名且值得信赖的名字,并且意味着该项目受到了真正知道他在做什么的人的一些监督。该项目也得到了NSF 的支持

然而,值得指出的是......
如果您在提交密码之前对客户端进行散列,那么散列就是密码,原始密码变得无关紧要。攻击者只需拦截散列即可模拟用户,如果该散列未经修改地存储在服务器上,则服务器以纯文本形式存储真实密码(散列)

因此,您的安全性现在变得更糟,因为您决定对以前受信任的方案添加自己的改进

@NickBrunt 如果您在服务器上进行散列,那么您不会保存传输的密码,但是除了传输原始密码而不是传输密码散列之外,您没有增加任何安全性,因为在任何一种情况下,您传输的都是真实密码。
2021-03-16 08:38:46
但是,如果您在服务器上再次散列,这种做法是完全合法的。
2021-03-17 08:38:46
没错,但这并不比发送可能在 Internet 上其他地方以明文形式使用的密码更糟糕。
2021-03-19 08:38:46
我需要使用客户端散列,因为我不希望服务器看到用户的明文密码。不是为了增加我提供的服务的安全性,而是为了用户。我不仅保存用户的哈希值,使用常量盐(每个用户常量,不是全局),而且每次登录时都会使用随机会话盐重新散列它,这确实在网络上提供了一点额外的安全性以防止嗅探。如果服务器受到威胁,游戏就结束了,但无论如何,对于任何不是真正的 p2p 都是如此。
2021-04-08 08:38:46
我同意@NickBrunt。如果攻击者读取可能只适合这个特定应用程序的随机哈希字符串,而不是可能在多个地方使用的原始密码,则更好。
2021-04-09 08:38:46

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest我发现这个使用内部 js module的片段:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder().encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    return hashHex;
}

请注意,crypto.subtle仅适用于httpslocalhost- 例如对于您的本地开发,python3 -m http.server您需要将此行添加到您的/etc/hosts0.0.0.0 localhost

重新启动 - 您可以打开localhost:8000工作crypto.subtle

失败对我来说器(Chrome 88) TypeError: Cannot read property 'digest' of undefined由于未知原因crypto.subtle仅在 HTTPS 上下文中定义,这使得它不适合一般使用。
2021-03-19 08:38:46
内置的“加密”库应该可以正常工作:nodejs.org/dist/latest-v11.x/docs/api/crypto.html
2021-04-03 08:38:46
这太棒了。谢谢你。Node.js 中是否有类似的 API 可用?
2021-04-05 08:38:46

对于那些感兴趣的人,这是使用以下方法创建 SHA-256 哈希的代码sjcl

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
这是最好的答案。我正在尝试加密解决方案,但它对我不起作用。
2021-03-15 08:38:46
对于那些想要在那里使用解决方案的人,这应该包含在已接受的答案中。(oop,即使他们自己的文档也没有这样的片段)
2021-03-26 08:38:46

Forge的 SHA-256 实现快速且可靠。

要在多个 SHA-256 JavaScript 实现上运行测试,请访问http://brillout.github.io/test-javascript-hash-implementations/

我机器上的结果表明,forge 是最快的实现,而且比接受的答案中提到的斯坦福 Javascript 加密库 (sjcl) 快得多。

Forge 是 256 KB 大,但提取 SHA-256 相关代码将大小减小到 4.5 KB,请参阅https://github.com/brillout/forge-sha256

我设法安装了它npm install node-forge,但是现在,如何使用库从字符串创建哈希foo
2021-03-27 08:38:46

不,没有办法使用浏览器 JavaScript 来提高密码安全性。我强烈建议您阅读这篇文章在你的情况下,最大的问题是鸡蛋问题:

提供 Javascript 密码学的“鸡蛋问题”是什么?

如果您不相信网络会提供密码,或者更糟的是,不相信服务器不会为用户保密,那么您就不能相信他们会提供安全代码。在您引入加密之前嗅探密码或阅读日记的攻击者只是在您这样做之后劫持加密代码。

[...]

为什么我不能使用 TLS/SSL 来传递 Javascript 加密代码?

你可以。这比听起来要难,但您可以使用 SSL 安全地将 Javascript 加密传输到浏览器。问题是,使用 SSL 建立了安全通道后,您不再需要 Javascript 加密;你有“真正的”密码学。

这导致了这个:

在 Javascript 中运行加密代码的问题在于,实际上加密所依赖的任何功能都可能被用于构建托管页面的任何内容静默覆盖。加密安全可以在过程的早期(通过生成虚假的随机数,或通过篡改算法使用的常量和参数)或稍后(通过将密钥材料返回给攻击者)或——在最有可能的情况下被撤销--- 完全绕过加密。

任何一段 Javascript 代码都没有可靠的方法来验证其执行环境。Javascript 加密代码不能问:“我真的在处理随机数生成器,还是攻击者提供的一些传真?” 它当然不能断言“除了我,作者,批准的方式之外,没有人被允许对这个加密秘密做任何事情”。这两个属性通常在其他使用加密的环境中提供,而在 Javascript 中是不可能的。

基本上问题是这样的:

  • 您的客户不信任您的服务器,因此他们想要添加额外的安全代码。
  • 该安全代码由您的服务器(他们不信任的服务器)提供。

或者,

  • 您的客户不信任 SSL,因此他们希望您使用额外的安全代码。
  • 该安全代码通过 SSL 提供。

注意:此外,SHA-256 不适合于此,因为它很容易暴力破解未加盐的非迭代密码如果您决定这样做,请寻找bcryptscryptPBKDF2 的实现

@Xenland 鸡和蛋的问题与您发送的请求没有任何关系。问题在于您的客户端正在使用通过不安全连接下载的JavaScript 代码来执行安全工作。将 JavaScript 安全发送到 Web 浏览器的唯一方法是通过 TLS 为其提供服务,一旦您这样做,您就无需再执行任何其他操作,因为您的连接已经是安全的。如果您使用的是攻击者可以替换的 JavaScript 代码,那么您的协议是什么并不重要。
2021-03-14 08:38:46
应该在客户端上使用 PBKDF2 是对的。对客户端 javascript 加密的愤怒假设初始页面和后续请求具有相同的安全属性。对于初始页面单独安装并且是静态的浏览器应用程序,这显然不是真的。对于任何具有永久缓存语义的页面来说也不是这样。在这些情况下,TOFU 与安装客户端程序时完全相同。在更复杂的设置中也是如此,其中静态和动态页面的服务在服务器上是分开的。
2021-03-15 08:38:46
避免系统管理员访问用户密码是合法的,因此在客户端进行散列可防止 DBA 或其他 IT 专业人员查看用户密码并尝试重新使用它来访问其他服务/系统,因为许多用户重复他们的密码.
2021-03-17 08:38:46
@Xenland 您所做的就是创建一个新密码,即“令牌 + 密码”。所有原始问题仍然适用。例如,攻击者可以用代码替换 JavaScript,向他们发送令牌 + 密码。
2021-03-17 08:38:46
这是不正确的。可以通过在客户端散列来提高密码安全性。只要在服务器端使用诸如 PBKDF2 之类的东西,SHA-256 就不适合也是错误的。第三,虽然 matasano 文章包含有用的见解,但妙语是错误的,因为我们现在拥有从根本上改变先有鸡还是先有蛋的问题的浏览器应用程序。
2021-03-31 08:38:46