Existe alguma implementação de javascript SHA-256 que geralmente é considerada confiável?

93

Estou escrevendo um login para um fórum e preciso fazer o hash da senha do lado do cliente em javascript antes de enviá-la para o servidor. Estou tendo problemas para descobrir em qual implementação SHA-256 posso realmente confiar. Eu esperava que houvesse algum tipo de script oficial que todos usassem, mas estou encontrando vários projetos diferentes, todos com suas próprias implementações.

Sei que usar a criptografia de outras pessoas é sempre um ato de fé, a menos que você esteja qualificado para revisá-la sozinho, e que não existe uma definição universal de "confiável", mas isso parece algo comum e importante o suficiente para que haja algum tipo de consenso sobre o que usar. Eu sou apenas ingênuo?

Edite já que aparece muito nos comentários: Sim, fazemos um hash mais rigoroso novamente no lado do servidor. O hashing do lado do cliente não é o resultado final que salvamos no banco de dados. O hashing do lado do cliente ocorre porque o cliente humano o solicita. Eles não deram um motivo específico, provavelmente eles apenas gostam de exagero.

jono
fonte
9
Para não sair do assunto, mas por que você está alterando a senha no lado do cliente?
Steve
5
@ddyer Nem perto. "Don't roll your own" se aplica a inventar seu próprio algoritmo, escrever sua própria implementação de um algoritmo, desenvolver seu próprio protocolo em cima de algoritmos de criptografia ou praticamente qualquer coisa acima usando uma abstração de alto nível disponível. Se você acha que estará seguro aderindo a um núcleo seguro, e apenas escrevendo código de cola, você terá um péssimo momento.
Stephen Touset
26
se você usar uma senha com hash sem um protocolo de desafio / resposta, a senha com hash É a senha e é realmente o mesmo que transmitir a senha em texto não criptografado.
ddyer
26
@ddyer Existe algum valor em proteger a senha de texto simples do usuário para todos os outros sites em que eles possam usá-la, se não para o nosso site em particular. É uma solução fácil que talvez não nos ajude, mas pode potencialmente ajudar o usuário se errarmos em algum lugar. E como eu disse, pedido do cliente, nada que eu possa fazer sobre isso, mesmo se eu quisesse.
jono
4
@Anorov Estou mais do que aberto para mudar minha mente :) mas, neste caso, eu realmente não entendo como seu ponto se aplica. Fazemos hash da senha duas vezes: uma no lado do cliente com um SHA-256 simples e outra no lado do servidor com algo mais exigente. O primeiro para proteger o texto simples no caso de MITM ou similar, e o segundo para proteção bruta. Mesmo que você obtivesse o banco de dados e o hash de administrador, não poderia usar isso diretamente para validar o login.
jono

Respostas:

110

A Stanford JS Crypto Library contém uma implementação de SHA-256. Embora a criptografia em JS não seja um empreendimento tão bem avaliado quanto outras plataformas de implementação, esta é pelo menos parcialmente desenvolvida por, e até certo ponto patrocinada por, Dan Boneh , que é um nome bem estabelecido e confiável em criptografia e significa que o projeto foi supervisionado por alguém que realmente sabe o que está fazendo. O projeto também é apoiado pela NSF .

Vale a pena ressaltar, entretanto ...
... que se você hash a senha do lado do cliente antes de enviá-la, então o hash é a senha , e a senha original se torna irrelevante. Um invasor precisa apenas interceptar o hash para personificar o usuário e, se esse hash estiver armazenado sem modificações no servidor, o servidor está armazenando a senha verdadeira (o hash) em texto simples .

Portanto, sua segurança agora está pior porque você decidiu adicionar suas próprias melhorias ao que antes era um esquema confiável.

tylerl
fonte
30
No entanto, se você fizer hash novamente no servidor, essa prática é perfeitamente legítima.
Nick Brunt
13
@NickBrunt se você hash no servidor, então você não está salvando a senha transmitida, mas você não adicionou nenhuma segurança além de transmitir a senha original ao invés de transmitir o hash de senha, já que em ambos os casos o que você está transmitindo é o senha real .
tylerl
54
É verdade, mas não é pior do que enviar uma senha que pode ser usada em outro lugar na Internet em texto simples.
Nick Brunt
15
Eu concordo com @NickBrunt. É melhor se o invasor ler uma string hash aleatória que pode caber apenas neste aplicativo específico, em vez da senha original que pode ser usada em vários lugares.
Aebsubis
12
Preciso usar o hashing do lado do cliente porque não quero que o servidor nunca veja a senha em texto simples do usuário. Não para aumentar a segurança do serviço que estou prestando, mas para o usuário. Não estou apenas salvando o hash do usuário salgado com um salt constante (constante por usuário, não globalmente), mas também fazendo um novo hash com um salt de sessão aleatório a cada login, o que oferece um pouco de segurança extra na rede contra farejamentos. Se o servidor ficar comprometido, o jogo termina, mas isso é verdade para qualquer coisa que não seja verdadeiramente P2P.
Hatagashira
49

Em https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest , encontrei este snippet que usa o módulo js interno:

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;
}

Observe que está crypto.subtledisponível apenas em httpsou localhost- por exemplo, para o seu desenvolvimento local, python3 -m http.servervocê precisa adicionar esta linha ao seu /etc/hosts: 0.0.0.0 localhost

Reinicie - e você pode abrir localhost:8000com o trabalho crypto.subtle.

Vitaly Zdanevich
fonte
2
Isso é incrível. Obrigado. Uma API comparável está disponível em Node.js?
Con Antonakos
2
A biblioteca interna
tytho
17

A implementação SHA-256 do Forge é rápida e confiável.

Para executar testes em várias implementações SHA-256 JavaScript, vá para http://brillout.github.io/test-javascript-hash-implementations/ .

Os resultados na minha máquina sugerem que o forge seja a implementação mais rápida e também consideravelmente mais rápida do que a Stanford Javascript Crypto Library (sjcl) mencionada na resposta aceita.

Forge tem 256 KB de tamanho, mas extrair o código relacionado ao SHA-256 reduz o tamanho para 4,5 KB, consulte https://github.com/brillout/forge-sha256

brilho
fonte
Consegui instalar com ele npm install node-forge, mas agora, como usar a biblioteca para criar um hash a partir da string foo?
usuário
15

Para os interessados, este é um código para criar hash SHA-256 usando sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Danny Sullivan
fonte
4
Isso deve ser incorporado na resposta aceita para aqueles que desejam usar a solução lá. (Caramba, mesmo seus próprios documentos não têm um trecho como este)
MagicLegend
Esta é a melhor resposta. Eu estava tentando a solução de criptografia, mas não funcionou para mim.
Augusto Gonzalez
11

Não, não há como usar o JavaScript do navegador para melhorar a segurança da senha. Eu recomendo fortemente que você leia este artigo . No seu caso, o maior problema é o problema do ovo da galinha:

Qual é o "problema do ovo da galinha" em fornecer criptografia Javascript?

Se você não confia na rede para fornecer uma senha ou, pior, não confia no servidor para não manter os segredos do usuário, você não pode confiar neles para fornecer o código de segurança. O mesmo invasor que estava farejando senhas ou lendo diários antes de você introduzir a criptografia está simplesmente sequestrando o código criptográfico depois de você fazer isso.

[...]

Por que não posso usar TLS / SSL para fornecer o código criptográfico Javascript?

Você pode. É mais difícil do que parece, mas você transmite criptografia Javascript com segurança para um navegador usando SSL. O problema é que, tendo estabelecido um canal seguro com SSL, você não precisa mais da criptografia Javascript; você tem criptografia "real".

O que leva a isso:

O problema com a execução de código criptográfico em Javascript é que praticamente qualquer função da qual a criptografia depende pode ser substituída silenciosamente por qualquer parte do conteúdo usado para construir a página de hospedagem. A criptografia de segurança pode ser desfeita no início do processo (gerando números aleatórios falsos ou adulterando constantes e parâmetros usados ​​por algoritmos) ou posteriormente (devolvendo o material da chave a um invasor), ou --- no cenário mais provável --- contornando a criptografia inteiramente.

Não existe uma maneira confiável para qualquer parte do código Javascript verificar seu ambiente de execução. O código criptográfico Javascript não pode perguntar: "Estou realmente lidando com um gerador de números aleatórios ou com algum fac-símile fornecido por um invasor?" E certamente não pode afirmar "ninguém tem permissão para fazer nada com este segredo criptográfico, exceto de maneiras que eu, o autor, aprovo". Essas são duas propriedades que geralmente são fornecidas em outros ambientes que usam criptografia e são impossíveis em Javascript.

Basicamente, o problema é este:

  • Seus clientes não confiam em seus servidores, então eles desejam adicionar código de segurança extra.
  • Esse código de segurança é fornecido por seus servidores (aqueles em que eles não confiam).

Ou alternativamente,

  • Seus clientes não confiam em SSL, então eles querem que você use um código de segurança extra.
  • Esse código de segurança é entregue via SSL.

Nota: Além disso, SHA-256 não é adequado para isso, já que é tão fácil usar a força bruta em senhas sem sal e não iteradas . Se você decidir fazer isso de qualquer maneira, procure uma implementação de bcrypt , scrypt ou PBKDF2 .

Brendan Long
fonte
1
Isso não está correto. A segurança da senha pode ser melhorada com hash no lado do cliente. Também é errado que o SHA-256 não seja adequado, desde que algo como PBKDF2 seja usado no lado do servidor. Em terceiro lugar, embora o artigo do matasano contenha uma visão útil, a conclusão está errada, pois agora temos aplicativos de navegador que mudam fundamentalmente a questão do ovo e da galinha.
user239558
2
Você está certo de que PBKDF2 deve ser usado no cliente. A raiva contra a criptografia javascript do lado do cliente pressupõe que a página inicial e as solicitações subsequentes tenham as mesmas propriedades de segurança. Isso obviamente não é verdade para aplicativos de navegador em que a página inicial é instalada separadamente e é estática. Também não é verdade para qualquer página com semântica de cache permanente. Nestes casos, é TOFU que é exatamente igual ao da instalação de um programa do lado do cliente. Isso também pode ser verdadeiro em configurações mais complexas, em que a exibição de páginas estáticas e dinâmicas é separada no servidor.
user239558
3
É legítimo evitar que os administradores de sistema tenham acesso às senhas dos usuários, então o hash no lado do cliente impede que DBAs ou outros profissionais de TI vejam as senhas dos usuários e tentem reutilizá-las para acessar outros serviços / sistemas, já que muitos usuários repetem suas senhas .
Yamada de
1
@Xenland Tudo o que você está fazendo é criar uma nova senha que é "token + senha". Todos os problemas originais ainda se aplicam. Por exemplo, um invasor pode substituir o JavaScript por um código para enviar o token + senha.
Brendan Long de
2
@Xenland O problema do ovo e da galinha não tem nada a ver com os pedidos que você envia. O problema é que seu cliente está usando um código JavaScript que baixou por meio de uma conexão insegura para fazer o trabalho de segurança. A única maneira de enviar JavaScript com segurança para um navegador da web é exibi-lo por TLS e, depois de fazer isso, não será necessário fazer mais nada porque sua conexão já está segura. Não importa qual seja o seu protocolo se você estiver usando código JavaScript que um invasor pode simplesmente substituir.
Brendan Long
10

Achei essa implementação muito fácil de usar. Também tem uma licença generosa no estilo BSD:

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

Eu precisava de uma maneira rápida de obter a representação hexadecimal de um hash SHA-256. Demorou apenas 3 linhas:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
Cobbzilla
fonte
1

Além da biblioteca de Stanford que tylerl mencionou. Achei o jsrsasign muito útil ( repositório Github aqui: https://github.com/kjur/jsrsasign ). Não sei exatamente o quão confiável é, mas usei sua API de SHA256, Base64, RSA, x509 etc. e funciona muito bem. Na verdade, também inclui a biblioteca de Stanford.

Se tudo o que você deseja fazer é SHA256, jsrsasign pode ser um exagero. Mas se você tiver outras necessidades na área relacionada, acho que é uma boa opção.

Tão distante
fonte
2
É mais seguro e rápido usar developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich
2
Concordo, mas para ser justo, respondi a esta pergunta em 2015, enquanto a especificação da API de criptografia da web do mozilla foi publicada em janeiro de 2017
Faraway