【问题标题】:How can I get the sha1 hash of a string in node.js?如何在 node.js 中获取字符串的 sha1 哈希?
【发布时间】:2011-10-22 11:38:37
【问题描述】:

我正在尝试创建一个用 node.js 编写的 websocket 服务器

要让服务器正常工作,我需要获取字符串的 SHA1 哈希值。

我必须做的在Section 5.2.2 page 35 of the docs中解释。

注意:例如,如果 "Sec-WebSocket-Key" 的值 客户端握手中的标头为"dGhlIHNhbXBsZSBub25jZQ==",服务器将附加字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"以形成 字符串"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"。然后,服务器将获取该字符串的 SHA-1 哈希,给出值 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea。然后将该值进行 base64 编码,得到值"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",该值将被返回 在"Sec-WebSocket-Accept" 标头中。

【问题讨论】:

  • 我会强烈推荐使用优秀的 socket.io 库而不是自己滚动。这不仅经过了广泛的测试和修补,而且通过各种方法支持大多数浏览器(即使是没有 WebSocket API 的浏览器)。
  • 未来访问者的一个很好的参考:stackoverflow.com/questions/9407892/…

标签: javascript node.js websocket


【解决方案1】:

查看crypto.createHash() function 以及相关的hash.update()hash.digest() 函数:

var crypto = require('crypto')
var shasum = crypto.createHash('sha1')
shasum.update('foo')
shasum.digest('hex') // => "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"

【讨论】:

  • 如果您查看tools.ietf.org/html/… 的示例,则预期的 base64 值为 "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" 。为此,您应该省略 shasum,digest() 的编码参数,以便返回缓冲区(而不是字符串)。然后,您可以调用该缓冲区的 .toString('base64') 并得到预期的答案。
【解决方案2】:

必填:SHA1 坏了,你可以compute SHA1 collisions for 45,000 USD。你应该使用sha256

var getSHA256ofJSON = function(input){
    return crypto.createHash('sha256').update(JSON.stringify(input)).digest('hex')
}

回答您的问题并制作 SHA1 哈希:

const INSECURE_ALGORITHM = 'sha1'
var getInsecureSHA1ofJSON = function(input){
    return crypto.createHash(INSECURE_ALGORITHM).update(JSON.stringify(input)).digest('hex')
}

然后:

getSHA256ofJSON('whatever')

getSHA256ofJSON(['whatever'])

getSHA256ofJSON({'this':'too'})

Official node docs on crypto.createHash()

【讨论】:

  • 好主意。但是请注意,所有对象(数组和 null 除外)都将具有相同的 sha1sum 值,因为默认情况下Object.toString() 返回[object Object]。所以sha1sum({}) === sha1sum({"foo":"bar"}) === sha1sum({"a":1})
  • sha1(JSON.stringify("some string")) => sha1("\"some string\"") 这绝对不是预期的,也不是跨平台的。有时更好是善的敌人。
  • 给定字符串的 sha1 在任何平台上都应该是相同的。您使用 JSON.stringify 的实现正在更改原始字符串,并且 sha1sum("abcd") 给出 f805c8fb0d5c466362ce9f0dc798bd5b3b32d512 任何人都会期望 81fe8bfe87576c3ecb22426f8e57847382917acf
  • @Pierre 这是一个很好的观点。鉴于您所说的,我认为将函数命名为 sha1sum 是不准确的——这显然比普通的 sha1 做得更多。我已经在答案中重命名了函数。
  • 由于 haveibeenpwned.com API 需要 SHA1 哈希,即使在今天也有完全正当的理由使用它。不过还是谢谢你的回答!
【解决方案3】:

防止问题的提示(坏哈希):

我体验过 NodeJS 正在对字符串的 UTF-8 表示进行哈希处理。其他语言(如 Python、PHP 或 PERL...)正在对字节字符串进行哈希处理。

我们可以添加 binary 参数来使用字节串。

const crypto = require("crypto");

function sha1(data) {
    return crypto.createHash("sha1").update(data, "binary").digest("hex");
}

sha1("Your text ;)");

您可以尝试使用:“\xac”、“\xd1”、“\xb9”、“\xe2”、“\xbb”、“\x93”等...

其他语言(Python、PHP、...):

sha1("\xac") //39527c59247a39d18ad48b9947ea738396a3bc47

Nodejs:

sha1 = crypto.createHash("sha1").update("\xac", "binary").digest("hex") //39527c59247a39d18ad48b9947ea738396a3bc47
//without:
sha1 = crypto.createHash("sha1").update("\xac").digest("hex") //f50eb35d94f1d75480496e54f4b4a472a9148752

【讨论】:

  • 'binary' - 'latin1' 的别名 nodejs.org/api/…
  • ^^ @JossefHarush 的评论非常重要!如果您不需要在散列之前将文本编码为 latin1(例如,为了与 PHP 兼容),并且您的文本有可能包含 latin1 范围之外的 Unicode 符号(例如 emoji!),请不要使用 binary !在编码中使用binarylatin1丢失信息 并增加冲突的可能性!用这两个试试上面的 sn-p 例如:
  • 所有哈希都是在二进制数据上完成的。您遇到的问题是您提到的其他语言没有使用 UTF-8,而不是相反。一旦您尝试对 Latin1 之外的内容进行哈希处理,这将变得非常明显。特别是在 PHP 的情况下,编码完全由源决定,例如硬编码文本的文本文件本身。 Perl 可能需要一些繁重的工作才能使用 UTF-8。
  • 我们不能对被散列的模式的字符集做任何假设,它必须是二进制的。没有人在乎碰撞,所有的实现都必须是确定性的,
【解决方案4】:

你可以使用:

  const sha1 = require('sha1');
  const crypt = sha1('Text');
  console.log(crypt);

安装:

  sudo npm install -g sha1
  npm install sha1 --save

【讨论】:

  • 您好 user944550,欢迎您。请考虑添加更多信息。
【解决方案5】:

请阅读并认真考虑我在您帖子的 cmets 中的建议。话虽如此,如果您仍然有充分的理由这样做,请查看this list of crypto modules for Node。它具有处理 sha1 和 base64 的模块。

【讨论】:

    【解决方案6】:

    使用 Node v15 中添加的新浏览器兼容、零依赖 SubtleCrypto API 回答

    const crypto = this.crypto || require('crypto').webcrypto;
    
    const sha1sum = async (message) => {
      const encoder = new TextEncoder()
      const data = encoder.encode(message)
      const hashBuffer = await crypto.subtle.digest('SHA-1', data)
      const hashArray = Array.from(new Uint8Array(hashBuffer));                     // convert buffer to byte array
      const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
      return hashHex;
    }
    
    sha1sum('foo')
      .then(digestHex => console.log(digestHex))
    
    // "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"

    节点沙盒:https://runkit.com/hesygolu/61564dbee2ec8600082a884d

    来源:

    【讨论】:

      猜你喜欢
      • 2020-12-22
      • 2018-08-03
      • 2014-12-25
      • 2013-08-28
      • 2011-08-18
      • 1970-01-01
      • 2011-08-24
      • 2011-12-27
      • 2016-10-12
      相关资源
      最近更新 更多