143

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

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

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

4

9 回答 9

133

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


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

然而,值得指出的是
……如果您在提交密码之前在客户端对密码进行哈希处理,那么哈希就是密码,原始密码变得无关紧要。攻击者只需要截取哈希来冒充用户,如果该哈希未经修改地存储在服务器上,那么服务器将以纯文本形式存储真正的密码(哈希) 。

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

于 2013-08-19T22:44:42.220 回答
122

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

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

于 2018-01-09T05:05:15.987 回答
32

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

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
于 2018-02-12T01:37:39.937 回答
21

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

于 2015-07-22T11:39:11.423 回答
14

不,没有办法使用浏览器 JavaScript 来提高密码安全性。我强烈推荐你阅读这篇文章。就您而言,最大的问题是鸡蛋问题:

交付 Javascript 密码学的“鸡蛋问题”是什么?

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

[...]

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

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

这导致了这一点:

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

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

基本上问题是这样的:

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

或者,

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

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

于 2013-08-19T22:14:19.563 回答
12

我发现这个实现非常容易使用。也有一个慷慨的 BSD 风格的许可证:

jsSHA:https ://github.com/Caligatio/jsSHA

我需要一种快速的方法来获取 SHA-256 哈希的十六进制字符串表示。只用了 3 行:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
于 2016-06-02T00:28:05.310 回答
1

除了 tylerl 提到的斯坦福库。我发现jsrsasign非常有用(这里的 Github 存储库:https ://github.com/kjur/jsrsasign )。我不知道它的可信度如何,但我使用了它的 SHA256、Base64、RSA、x509 等 API,并且效果很好。事实上,它也包括斯坦福库。

如果您只想做 SHA256,那么 jsrsasign可能会有点过头了。但如果你在相关领域有其他需求,我觉得还是很合适的。

于 2015-12-11T22:29:03.043 回答
0

可以使用 CryptoJS - https://www.npmjs.com/package/crypto-js

import sha256 from 'crypto-js/sha256'

const hash = sha256('Text')
于 2022-01-18T00:35:25.070 回答
0

ethers.js 有一个 SHA256 ( https://docs.ethers.io/v5/api/utils/hashing/ )

const { ethers } = require('ethers');
ethers.utils.sha256(ethers.utils.toUtf8Bytes('txt'));
于 2022-02-16T08:19:32.257 回答