Como posso obter o hash sha1 de uma string em node.js?

108

Estou tentando criar um servidor websocket escrito em node.js

Para fazer o servidor funcionar, preciso obter o hash SHA1 de uma string.

O que tenho que fazer é explicado na Seção 5.2.2 página 35 dos documentos .

NOTA: Por exemplo, se o valor do "Sec-WebSocket-Key" cabeçalho no handshake do cliente fosse "dGhlIHNhbXBsZSBub25jZQ==", o servidor acrescentaria a sequência "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"para formar a sequência "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11". O servidor então pegaria o hash SHA-1 desta string, dando o valor 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. Esse valor é então codificado em base64, para fornecer o valor "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", que seria retornado no "Sec-WebSocket-Accept"cabeçalho.

Eric
fonte
9
Eu altamente recomendável usar o excelente socket.io biblioteca, em vez de rolar seus próprios. Isso não apenas foi amplamente testado e corrigido, mas também oferece suporte à maioria dos navegadores (inclusive aqueles sem a API WebSocket) por meio de vários métodos.
Alex Turpin
1
Uma boa referência para os futuros visitantes: stackoverflow.com/questions/9407892/…
Damodaran

Respostas:

32

Obrigatório: SHA1 está quebrado , você pode calcular colisões SHA1 por 45.000 USD . Você deve usar sha256:

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

Para responder à sua pergunta e fazer um hash SHA1:

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

Então:

getSHA256ofJSON('whatever')

ou

getSHA256ofJSON(['whatever'])

ou

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

Documentos oficiais do nó em crypto.createHash()

mikemaccana
fonte
7
Boa ideia. Observe, no entanto, que todos os objetos (exceto arrays e null) terão o mesmo valor sha1sum, pois Object.toString()retorna [object Object]por padrão. Então sha1sum({})=== sha1sum({"foo":"bar"})=== sha1sum({"a":1}), etc.
maerics
sha1 (JSON.stringify ("alguma string")) => sha1 ("\" alguma string \ "") que não é absolutamente esperado e não é uma plataforma cruzada. Às vezes, o melhor é inimigo do bom.
Pierre
3
espera-se que sha1 de uma determinada string seja o mesmo em qualquer plataforma. Sua implementação usando JSON.stringify está alterando a string original e sha1sum ("abcd") dá f805c8fb0d5c466362ce9f0dc798bd5b3b32d512, onde qualquer pessoa esperaria 81fe8bfe87576c3ecb22426f8e57847382917acf
Pierre
2
@Pierre Esse é um excelente ponto. Acho que nomear a função sha1sumé impreciso, dado o que você disse - isso claramente faz mais do que um sha1 normal faria. Renomeei a função na resposta.
mikemaccana
Não há até hoje nenhuma colisão conhecida para o SHA-1 de 80
cartuchos
8

Por favor, leia e considere fortemente meu conselho nos comentários de sua postagem. Dito isso, se você ainda tiver um bom motivo para fazer isso, verifique esta lista de módulos criptográficos para o Node . Ele possui módulos para lidar com sha1 e base64.

Alex Turpin
fonte
7

Dicas para evitar problemas (hash ruim):

Percebi que o NodeJS está fazendo o hash da representação UTF-8 da string. Outras linguagens (como Python, PHP ou PERL ...) estão fazendo o hash da string de bytes.

Podemos adicionar um argumento binário para usar a string de bytes.

const crypto = require("crypto");

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

sha1("Your text ;)");

Você pode tentar com: "\ xac", "\ xd1", "\ xb9", "\ xe2", "\ xbb", "\ x93", etc ...

Outras linguagens (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
A-312
fonte
1
'binary'- Alias ​​para 'latin1' nodejs.org/api/…
Jossef Harush
1
^^ Observação extremamente importante de @JossefHarush! Se você não precisa codificar especificamente o texto como latin1 antes de hash (por exemplo, exatamente para compatibilidade com PHP), e há qualquer chance de que seu texto contenha símbolos Unicode fora do intervalo latin1 (por exemplo, emoji!), Não use binary! Usar binaryou latin1na codificação perderá informações e aumentará a probabilidade de colisões! Experimente o snippet acima com estes dois, por exemplo: e
cbr
Todos os hashes são feitos em dados binários. O problema que você está enfrentando é que os outros idiomas mencionados não estão usando UTF-8, não o contrário. Isso ficará muito claro quando você tentar hash algo fora de Latin1. No caso do PHP em particular, a codificação é inteiramente determinada pela fonte, como o próprio arquivo de texto para texto codificado. Perl pode precisar de um trabalho pesado para usar UTF-8.
Ryan Hanekamp
3

Você pode usar:

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

Para instalar:

  sudo npm install -g sha1
  npm install sha1 --save
user944550
fonte
Olá, user944550, bem-vindo. Por favor, considere adicionar mais informações.
Tiago Martins Peres 李大仁 04/01