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


95

我正在为论坛编写登录名,并且需要先在javascript中对密码客户端进行哈希处理,然后再将其发送到服务器。我在弄清楚我可以信任的SHA-256实现方面遇到了麻烦。我期望每个人都可以使用某种权威的脚本,但是我发现许多不同的项目都有自己的实现。

我意识到使用他人的加密货币始终是信念的飞跃,除非您有资格自己审查它,并且没有“信任”的通用定义,但这似乎很普遍且重要,应该有某种意义关于使用什么的共识。我只是天真吗?

编辑,因为注释中包含很多内容:是的,我们在服务器端再次进行了更严格的哈希处理。客户端哈希不是我们保存在数据库中的最终结果。客户端哈希是因为人类客户端请求它。他们没有给出具体的原因,也许他们只是喜欢过度杀伤。


9
不是为了摆脱话题,而是为什么要在客户端哈希密码?
史蒂夫·

5
@ddyer甚至没有关闭。“不要推出自己的”适用于你自己发明的算法,编写自己的实现算法,开发上的加密算法,或者上面可使用作为高层次的抽象相当多的东西顶自己的协议。如果您认为可以安全地坚持一个安全的核心,并且只编写胶水代码,那将是一段糟糕的时光。
Stephen Touset

26
如果您使用不带质询/响应协议的哈希密码,则哈希密码就是密码,它与以明文形式传输密码实际上是相同的。
ddyer

26
@ddyer保护用户可能在其上使用的所有其他网站的纯文本密码(如果不是特别针对我们的网站)具有一定的价值。这是一个简单的解决方法,可能对我们没有帮助,但是如果我们在某个地方搞砸了,可能会对用户有所帮助。就像我说的,客户要求,即使我愿意,我也无能为力。
jono,2013年

4
@Anorov我很愿意改变主意:)但在这种情况下,我不太了解您的观点如何适用。我们对密码进行两次哈希处理:一次在客户端使用简单的SHA-256,一次在服务器端使用更苛刻的密码。第一个是在MITM或类似的情况下保护明文,第二个是用于粗暴保护。即使您拥有数据库和admin哈​​希,也无法直接使用它来验证登录。
jono 2013年

Answers:


111

斯坦福JS加密库包含SHA-256的实现。尽管JS中的加密并没有像其他实现平台那样经过严格的审查,但该平台至少部分是由Dan Boneh开发的,并在一定程度上是由Dan Boneh开发的,Dan Boneh是加密技术中公认的知名人士,这表示该项目受到实际上知道自己在做什么的人的监督。NSF也支持该项目。

但是,值得指出的是……
如果在提交之前对客户端的密码进行哈希处理,则哈希值就是password,原始密码就变得无关紧要了。攻击者仅需拦截哈希即可模拟用户,并且如果该哈希未经修改地存储在服务器上,则服务器将以纯文本格式存储真实密码(哈希)

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


31
但是,如果再次在服务器上进行哈希处理,则此做法完全合法。
尼克·布鲁特

13
@NickBrunt如果您在服务器上进行哈希处理,那么您将不会保存传输的密码,但是除了传输原始密码(而不是传输密码哈希)之外,您没有添加任何安全措施,因为无论哪种情况,您传输的都是真实密码。
tylerl

54
的确如此,但是这并不比发送密码(在互联网上其他地方以明文形式使用)更糟糕。
尼克·布伦特

15
我同意@NickBrunt。最好是,攻击者读取一个仅适用于此特定应用程序的随机哈希字符串,而不是在多个地方使用的原始密码。
Aebsubis 2014年

12
我需要使用客户端哈希,因为我不希望服务器看到用户的纯文本密码。不是为我提供的服务增加安全性,而是针对用户。我不仅要用恒定的盐(每位用户恒定,而不是全局的)将用户的哈希值保存成盐,而且每次登录时都使用随机的会话盐来重新哈希它,这确实为网络提供了防嗅探的额外安全性。如果服务器受到威胁,那就结束了,但是对于任何不是p2p的东西都是如此。
Hatagashira 2014年

49

https://developer.mozilla.org/zh-CN/docs/Web/API/SubtleCrypto/digest上,我找到了使用内部js模块的代码段:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder('utf-8').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 => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

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

重新启动-您可以开始localhost:8000使用crypto.subtle


2
这太棒了。谢谢。Node.js中是否有类似的API?
Con Antonakos

2
内置的“ crypto”库应该可以正常工作:nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho

17

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

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

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

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


我设法用来安装它npm install node-forge,但是现在,如何使用库从字符串创建哈希foo
用户

15

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

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)

4
对于那些想在那里使用解决方案的人,应该将其合并到接受的答案中。(哎呀,甚至他们自己的文档也没有这样的片段)
MagicLegend '18

这是最好的答案。我正在尝试加密解决方案,但对我而言不起作用。
奥古斯托·冈萨雷斯

11

不,无法使用浏览器JavaScript来提高密码安全性。我强烈建议您阅读本文。在您的情况下,最大的问题是鸡肉问题:

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

如果您不信任网络提供密码,或者更糟糕的是,不信任服务器不保留用户密码,则不能信任他们提供安全代码。在您介绍加密货币之前嗅探密码或阅读日记的同一名攻击者只是在这样做之后劫持了加密代码。

[...]

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

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

这导致:

在Javascript中运行加密代码的问题在于,几乎加密所依赖的任何功能都可以被用于构建托管页面的任何内容无声地覆盖。可以在此过程的早期(通过生成伪造的随机数,或通过篡改算法使用的常量和参数)取消加密安全性,或在以后(通过将关键材料归还攻击者)或在最可能的情况下取消加密安全性。---完全绕开了加密货币。

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

基本上问题是这样的:

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

或者,

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

注意:此外,SHA-256也不适用于此,因为它很容易暴力破解未加盐的非迭代密码。如果仍然决定执行此操作,请寻找bcryptscryptPBKDF2的实现


1
这是不正确的。可以通过在客户端进行哈希来提高密码安全性。只要在服务器端使用了PBKDF2之类的东西,SHA-256就不合适,这也是错误的。第三,尽管matasano文章包含有用的见解,但强调点是错误的,因为我们现在拥有的浏览器应用程序从根本上改变了“鸡与蛋”的问题。
user239558 2013年

2
您应该在客户端上使用PBKDF2是正确的。对客户端javascript加密的愤怒假定初始页面和后续请求具有相同的安全性属性。对于初始页面是单独安装且静态的浏览器应用程序,显然不是这样。对于任何具有永久缓存语义的页面来说,也并非​​如此。在这些情况下,正是TOFU与安装客户端程序时完全相同。在更复杂的设置中(服务器上的静态和动态页面的服务分开),这也是正确的。
user239558 2013年

3
避免系统管理员可以访问用户密码是合法的,因此,由于许多用户重复输入密码,因此在客户端进行哈希运算可以阻止DBA或其他IT专业人员查看用户密码并尝试重用它来访问其他服务/系统。 。
山田

1
@Xenland您要做的就是创建一个新的密码,即“令牌+密码”。所有原始问题仍然适用。例如,攻击者可以将JavaScript替换为代码,以向其发送令牌+密码。
布伦丹·朗

2
@Xenland鸡肉和鸡蛋问题与您发送的请求没有任何关系。问题是您的客户端正在使用通过不安全连接下载的 JavaScript代码来执行安全性工作。安全地将JavaScript发送到Web浏览器的唯一方法是通过TLS提供服务,一旦这样做,您就不需要做任何其他事情,因为您的连接已经很安全了。如果您使用的是攻击者可以替换的JavaScript代码,则协议的大小无关紧要。
布伦丹·朗

10

我发现此实现非常易于使用。还有一个慷慨的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");

1

除了tylerl提到的斯坦福大学图书馆。我发现jsrsasign非常有用(Github 仓库在这里:https : //github.com/kjur/jsrsasign)。我不知道它到底有多可信,但是我使用了它的API,如SHA256,Base64,RSA,x509等,而且效果很好。实际上,它也包括斯坦福图书馆。

如果您只想做SHA256,那么jsrsasign可能会大材小用。但是,如果您在相关领域还有其他需求,我认为这很合适。



2
同意,但为了公平起见,我在2015年mozilla的网络加密API规范发布时回答了这个问题,该规范于2017
-Faraway
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.