É mais seguro hash uma senha várias vezes?

43

Eu li algumas vezes que, ao armazenar senhas, é uma boa prática 'duplicar hash' as ​​strings (por exemplo, com md5 e sha1, ambos com sais, obviamente).

Acho que a primeira pergunta é: "isso está realmente correto?" Caso contrário, descarte o restante desta pergunta :)

A razão pela qual pergunto é que, diante disso, eu diria que isso faz sentido. No entanto, quando penso sobre isso, toda vez que um hash é revisado (possivelmente com algo a mais) tudo o que posso ver é que há uma redução no limite superior da 'singularidade' final ... esse limite está relacionado a a entrada inicial.

Deixe-me colocar de outra maneira: temos x número de strings que, quando hash, são reduzidos a y strings possíveis. Ou seja, há colisões no primeiro conjunto. Agora, vindo do segundo conjunto para o terceiro, não é possível que ocorra a mesma coisa (ou seja, colisões no conjunto de todas as possíveis seqüências de caracteres 'y' que resultam no mesmo hash no terceiro conjunto)?

Na minha cabeça, tudo o que vejo é um 'funil' para cada chamada de função hash, 'canalizando' um conjunto infinito de possibilidades para um conjunto finito e assim por diante, mas obviamente cada chamada está trabalhando no conjunto finito anterior a ela, dando-nos uma configure não maior que a entrada.

Talvez um exemplo explique minhas divagações? Pegue 'hash_function_a' que fornecerá 'a' e 'b' o hash '1' e dará 'c' e 'd' o hash '2'. Usando esta função para armazenar senhas, mesmo que a senha seja 'a', eu poderia usar a senha 'b'.

Pegue 'hash_function_b' que fornecerá '1' e '2' o hash '3'. Se eu fosse usá- lo como um 'hash secundário' após 'hash_function_a', mesmo se a senha for 'a' eu poderia usar 'b', 'c' ou 'd'.

Além disso, entendo que os sais devem ser usados, mas eles realmente não mudam o fato de que cada vez que mapeamos entradas 'x' para saídas 'menores que x'. Eu não acho.

Alguém por favor pode me explicar o que estou perdendo aqui?

Obrigado!

EDIT: para o que vale a pena, eu não faço isso sozinho, uso bcrypt. E não estou realmente preocupado em saber se é útil ou não usar 'ciclos' para um 'hacker'. Eu realmente estou apenas pensando se o processo reduz ou não a 'segurança' do ponto de vista da colisão de hash.

Narciso
fonte
2
@ S.Lott: Eu realmente não vejo como isso responde à pergunta real, no entanto ... tudo o que diz é "não faça você mesmo, use essa coisa" ou "é bom levar tempo!". .. nenhuma das respostas "é realmente mais segura". Novamente, a menos que esteja faltando alguma coisa.
Narciso
@MetalMikester: Sim, isso foi o artigo de ontem: thedailywtf.com/Articles/Bulletproof-Encryption.aspx
FrustratedWithFormsDesigner
Isso não é tópico de segurança de TI, mas parece uma boa combinação para criptografia. De fato, parece extremamente semelhante a essa pergunta lá .
Conheço uma empresa que queria usar sem sal MD5(password). Dissemos que não é seguro, então eles sugeriram o uso MD5(MD5(password))...
configurator:
A resposta aceita não é a resposta correta!
Markus

Respostas:

27

Isso é mais adequado em security.stackexchange, mas ...

O problema com

hash1(hash2(hash3(...hashn(pass+salt)+salt)+salt)...)+salt)

é que isso é tão forte quanto a função de hash mais fraca da cadeia. Por exemplo, se hashn (o hash mais interno) causar uma colisão, toda a cadeia de hash causará uma colisão ( independentemente do que os outros hashes estejam na cadeia ).

Uma cadeia mais forte seria

hash1(hash2(hash3(...hashn(pass + salt) + pass + salt) + pass + salt)...) + pass + salt)

Aqui evitamos o problema de colisão inicial e geramos essencialmente um sal que depende da senha do hash final.

E se um passo na cadeia colidir, não importa, porque no próximo passo a senha é usada novamente e deve fornecer um resultado diferente para senhas diferentes.

catraca arrepiante
fonte
Então, agora, vejo que adicionar "senha + sal" como sal à próxima rodada de hash pode estar aumentando a quantidade de 'coisas' que poderiam entrar no funil, agora só preciso entender por 'quanto'. Obrigado.
Narciso
Na verdade, acho que entendi agora: forçar a senha em cada camada do hash, na verdade, reduz o número de possíveis colisões, exigindo essencialmente 'a senha de colisão' e a senha real para ter hashes correspondentes em cada 'chamada' , direito? Estou pensando que estava faltando a parte 'coloque a senha em cada camada'! Obrigado novamente.
Narciso
@Narcissus não prob e que também tem a vantagem de permitir que mais fracos hash interiores (desde que o hash exteriores / final são forte) como o hash internas são apenas gerar o sal para a próxima passagem
anormal de roquete
hum, acho que o problema é um pouco maior (e mais profundo) que isso. Com ataques do arco-íris, você pode apenas gerar tabelas considerando todos os seus hashes, e o problema permanece.
Woliveirajr 20/10/11
4
@ Narciso: em uma resposta muito curta: Sim, não é mais seguro. Sim, provavelmente é ainda menos seguro.
Woliveirajr 21/10
54

Usar algoritmos de hash diferentes é uma má idéia - reduzirá a entropia em vez de aumentá-la.

No entanto, supondo que você tenha um algoritmo de hash criptograficamente forte e um bom sal, aplicar a mesma função de hash várias vezes torna o processo de hash mais caro em termos de computação. O benefício disso é que, quando outros meios de decifrar o hash da senha falham (suposições, ataques de dicionário, tabelas arco-íris etc.), e o invasor é forçado a usar técnicas de força bruta, leva mais tempo para tentar cada senha, simplesmente porque eles precisam aplicar a mesma função de hash com mais frequência. Portanto, se uma rodada de hash exigir um mês de força bruta, aplicá-la doze vezes aumentaria o tempo estimado para um ano.

Algoritmos de hash recentes como o bcrypt se baseiam nessa idéia; eles contêm um parâmetro para controlar a complexidade computacional do hash, para que você possa escalá-lo à medida que o hardware acelera: quando o hardware se torna mais rápido por um fator de dois, você aumenta a complexidade para compensar, de modo que o tempo necessário para forçar sua força bruta hashes permanece aproximadamente constante.

tdammers
fonte
2
Essa é a resposta correta!
Markus
@markus: de acordo com a discussão que li, isso só está correto por causa da adição "e um bom sal" que é abordado pela resposta aceita, não é? Por que este está correto e a resposta aceita não?
Narciso
É a resposta correta, porque o único motivo para aplicar a mesma função de hash várias vezes (parametrizado) é que você pode usar essa iteração para ajustar o hardware mais rápido. Caso contrário, não há benefício em fazer hash várias vezes.
Mark12 dez12
@ Narciso A chave aqui é entropia. Há uma diferença entre fazer hash muitas vezes usando o mesmo método e fazer hash muitas vezes usando métodos diferentes.
sakisk
3

Não tente escrever seu próprio esquema de hash de senha, a menos que esteja disposto a fazer um curso de criptografia e / ou engenharia de segurança.

Você deve usar uma implementação bem estabelecida de hash de senha que, por sua vez, deve usar uma função de derivação de chave ( KDF ), como PBKDF2, bcrypt, scrypt ou o Argon2 mais recente.

Os bons KDFs incluem um fator de trabalho, geralmente várias iterações, para aumentar o custo de ataques offline. Pode-se dizer que esses KDFs hash a senha várias vezes, usando o mesmo algoritmo de cada vez. Não faz sentido usar o algoritmo de digestão múltipla de mensagens, como apontado por outros.

Erwan Legrand
fonte
1
o link https://crackstation.net/hashing-security.htm é bloqueada pelo firewall
mosquito
1
@gnat Por algum motivo, perdi seu comentário sobre o URL bloqueado. Substituí-o por um link para a Wikipedia.
Erwan Legrand
2

Em geral, você não precisa usar mais de um algoritmo de hash.

O que você precisa fazer é:

Use salt: salt não é usado apenas para tornar sua senha mais segura , é usada para impedir o ataque da tabela do arco-íris. Dessa forma, alguém terá um trabalho mais difícil tentando pré-calcular o hash das senhas armazenadas em seu sistema.

Use várias interações: em vez de fazer apenas SHA (senha + sal), faça SHA (SHA (SHA (SHA (SHA (... SHA (senha + sal)))))). Ou, para representar de outra maneira:

hash = sha(password + salt)
for i=1 , i=5000, i++ {
    hash = sha(hash + salt);
}

E, finalmente, escolha uma boa função de hash. SHA, MD5, etc, não são bons porque são muito rápidos . Como você deseja usar o hash para proteção, é melhor usar hashes mais lentos. Dê uma olhada no Bcrypt , PBKDF2 ou Scrypt , por exemplo.

edit : após as observações, vamos tentar ver alguns pontos (desculpe, longa explicação para chegar ao fim, porque isso pode ajudar outras pessoas a procurar respostas semelhantes):

Se seu sistema estiver seguro, como ninguém jamais terá acesso à senha armazenada, você não precisará de hash. A senha seria secreta, ninguém a conseguiria.

Mas ninguém pode garantir que o banco de dados com as senhas será roubado. Roube o banco de dados, tenha todas as senhas. Ok, seu sistema e sua empresa sofrerão todas as conseqüências disso. Portanto, poderíamos tentar evitar que essa senha vazasse.

AVISO : não estamos preocupados com ataques online neste momento. Para um ataque on-line, a melhor solução é desacelerar após senhas ruins, bloquear a conta após algumas tentativas, etc. O ataque online é uma questão de diminuir a velocidade das entradas de senha .

Então, voltando ao don't let them take my plain passwordsproblema. A resposta é simples: não os armazene como texto simples. OK, entendi.

Como evitar isso?

Criptografe a senha (?). Mas, como você sabe, se você criptografá-lo, poderá descriptografá-lo novamente, se tiver a chave adequada. E você vai acabar com o problema de "onde esconder" a chave. Hum, nada bom, já que eles conseguiram seu banco de dados, eles podem pegar sua chave. Ok, não vamos usá-lo.

Então, outra abordagem: vamos transformar a senha em outra coisa que não pode ser revertida e armazená-la. E para verificar se a senha fornecida está correta, fazemos o mesmo processo novamente e verificamos se os dois valores transformados correspondem. Se eles corresponderem = a boa senha foi fornecida.

Ok, até agora tudo bem. Vamos usar um pouco de hash MD5 na senha. Mas ... se alguém tiver nosso valor de senha armazenado em hash, ele poderá ter muita energia do computador para calcular o hash MD5 de todas as senhas possíveis (força bruta), para encontrar a senha original. Ou, pior ainda, ele pode armazenar todo o MD5 de todas as combinações de caracteres e encontrar facilmente a senha. Então, faça muitas interações, a coisa HASH (HASH (HASH ())), para dificultar, porque levará mais tempo.

Mas mesmo isso pode ser contornado, a mesa arco-íris foi criada exatamente para acelerar contra esse tipo de proteção.

Então, vamos usar um pouco de sal por cima. Dessa forma, a cada interação, o sal é usado novamente. Quem tentar atacar suas senhas terá que gerar a tabela arco-íris, considerando que o sal é adicionado a cada vez. E quando ele gerar essa tabela do arco-íris, uma vez que foi gerada com um sal, ele precisará calcular novamente com o outro sal; portanto, terá que gastar algum tempo para cada senha (= cada sal). O Salt não adicionará "mais complexidade" à senha, apenas fará com que o invasor perca tempo gerando a tabela arco-íris; se você usar um sal para cada senha, a tabela de um sal será inútil para outra senha.

E usar mais de um hash terá ajudado aqui? Não. A pessoa que gerar um ataque arco-íris específico poderá gerá-lo usando um ou mais hashes, de qualquer maneira.

E usar mais de um hash pode levar você a um problema: é tão seguro quanto o hash mais fraco que você usa. Se alguém encontrar colisões em um algoritmo de hash, é esse hash que será explorado, a qualquer momento do processo de iteração, para quebrar a senha. Portanto, você não ganha nada usando mais algoritmos de hashes, é melhor escolher apenas um bom algo. e use-o. E se você ouvir que foi quebrado, pense em como o alterará em seu aplicativo.

E por que usar bcrypt ou algo assim (você diz que usa): porque o invasor terá que gastar mais tempo gerando as tabelas. É por isso que usar o MD5 + wait (3 segundos) não ajuda: o ataque estará offline, de qualquer maneira, para que o invasor possa gerar as tabelas sem o (atraso de 3 segundos).

woliveirajr
fonte
2
Opa! Desculpe, meu comentário (sobre deliberadamente tornar o hash mais lento via parâmetro de tempo limite) não foi feito para ser levado a sério ... Acho que tenho lido muito o Dilbert ultimamente.
FrustratedWithFormsDesigner
2
sha (sha (sha (...))) não é mais seguro que sha. Se a entropia da função sha não for máxima, isso é menos seguro.
deadalnix
1
@deadalnix: é importante mencionar que não facilita a recuperação da senha original, mas facilita a geração de uma senha colidida, que é tudo o que é necessário.
Bryan Boettcher
1
@deadalnix, li esse comentário na voz de Dale Gribble .
quer
1
@deadalnix: o objetivo de sha (sha (sha ())) não é, e nunca foi, adicionar mais entropia. A entropia é feita apenas pelo usuário inicial, escolhendo sua senha; todo o resto (hash, sal, etc.) é apenas para desacelerar um ataque de força bruta. Se alguém é capaz de obter o seu banco de dados contendo as senhas, ele provavelmente irá obter o código usado para criptografar a senha, bem como, de modo que qualquer sal embutido também é conhecido
woliveirajr
-1

Meu entendimento é que o uso de vários algoritmos de hash é derrotar as tabelas do arco-íris . Usar um bom sal também funciona, mas acho que é um segundo nível de proteção.

jiggy
fonte
4
Bem, isso não muda muito. A função «múltiplo hash» pode ser vista como uma e tratada como tal.
deadalnix
2
O uso de várias técnicas ou iterações de hash não ajuda a derrotar as tabelas do arco-íris. Se um invasor tiver seu banco de dados e o método usado para gerar os hashes, ele poderá gerar uma tabela arco-íris para atacar todas as senhas no banco de dados. O SALTS evita ataques à tabela arco-íris, pois evita que o invasor gere um único dicionário de pesquisa para atacar, digamos, todas as senhas de 8 caracteres ou menos.
Erik
-1

Isso não é mais seguro. No entanto, você tem um protocolo de identificação baseado em hash várias vezes com a mesma função.

Isso é assim. O valor armazenado é hash ^ n (aprovado) no computador A. A solicita a B para se autenticar e fornece a B o número inteiro n. B faz o cálculo hash ^ (n-1) (passa) e envia de volta para A.

Uma verificação que hash (hash ^ (n-1) (aprovado)) == hash ^ n (aprovado). Se for verdade, a autenticação é feita. Mas então, um hash de loja ^ (n-1) (aprovado) e a próxima autenticação fornecerão B n-1 em vez de n.

Isso garante que a senha nunca seja trocada de maneira clara, que A nunca saiba qual é a senha e que a autenticação seja protegida pela reprodução. No entanto, isso tem a desvantagem de exigir senha com vida útil limitada. Quando n atingir o valor 2, uma nova senha deve ser escolhida após a autenticação.

Outro uso de vários hash é a ferramenta HMAC, para garantir a autenticação e a integridade de uma solicitação. Para mais informações sobre o HMAC, consulte http://en.wikipedia.org/wiki/HMAC .

A maior parte do uso de vários hash no mundo real é um exagero. No seu caso, parece ser. Observe que, se você usar várias funções de hash, elas nem todas terão a mesma entropia, portanto, isso reduzirá a força do hash. Por exemplo, md5 tem menos entropia que sha1, portanto, usar sha1 em um md5 não melhorará a força do hash. A força geralmente será igual à força da função hash mais fraca.

deadalnix
fonte