Atualização: Observe que não estou perguntando o que é um sal, o que é uma tabela de arco-íris, o que é um ataque de dicionário ou qual é o propósito de um sal. Estou perguntando: Se você conhece os usuários salt e hash, não é muito fácil calcular sua senha?
Eu entendo o processo, e eu mesmo o implemento em alguns de meus projetos.
s = random salt
storedPassword = sha1(password + s)
No banco de dados que você armazena:
username | hashed_password | salt
Cada implementação de sal que vi adiciona o sal no final da senha ou no início:
hashed_Password = sha1(s + password )
hashed_Password = sha1(password + s)
Portanto, um ataque de dicionário de um hacker que vale a pena (ha ha) simplesmente executaria cada palavra-chave contra os sais armazenados nas combinações comuns listadas acima.
Certamente, a implementação descrita acima simplesmente adiciona outra etapa para o hacker, sem realmente resolver o problema subjacente. Que alternativas existem para contornar esse problema, ou estou entendendo mal o problema?
A única coisa que posso pensar em fazer é ter um algoritmo de combinação secreto que liga o salt e a senha em um padrão aleatório, ou adiciona outros campos de usuário ao processo de hash, o que significa que o hacker teria que ter acesso ao banco de dados E código para amarrar para que um ataque de dicionário seja frutífero. (Atualização, conforme apontado nos comentários, é melhor presumir que o hacker tenha acesso a todas as suas informações, então provavelmente não é o melhor).
Deixe-me dar um exemplo de como proponho que um hacker hackearia um banco de dados do usuário com uma lista de senhas e hashes:
Dados de nosso banco de dados hackeado:
RawPassword (not stored) | Hashed | Salt
--------------------------------------------------------
letmein WEFLS... WEFOJFOFO...
Dicionário de senha comum:
Common Password
--------------
letmein
12345
...
Para cada registro de usuário, faça um loop nas senhas comuns e faça um hash para elas:
for each user in hacked_DB
salt = users_salt
hashed_pw = users_hashed_password
for each common_password
testhash = sha1(common_password + salt)
if testhash = hashed_pw then
//Match! Users password = common_password
//Lets visit the webpage and login now.
end if
next
next
Espero que isso ilustre muito melhor o meu ponto.
Dadas 10.000 senhas comuns e 10.000 registros de usuário, precisaríamos calcular 100 milhões de hashes para descobrir o máximo possível de senhas de usuário. Pode demorar algumas horas, mas não é realmente um problema.
Atualização sobre a teoria do cracking
Assumiremos que somos um host corrompido, que tem acesso a um banco de dados de hashes e sais SHA1, junto com seu algoritmo para combiná-los. O banco de dados possui 10.000 registros de usuários.
Esse site afirma ser capaz de calcular 2.300.000.000 hashes SHA1 por segundo usando a GPU. (No mundo real, a situação provavelmente será mais lenta, mas por enquanto usaremos essa figura citada).
(((95 ^ 4) / 2300000000) / 2) * 10000 = 177 segundos
Dada uma gama completa de 95 caracteres ASCII imprimíveis, com um comprimento máximo de 4 caracteres, dividido pela taxa de cálculo (variável), dividido por 2 (assumindo que o tempo médio para descobrir a senha exigirá em média 50% das permutações) para 10.000 usuários levariam 177 segundos para descobrir as senhas de todos os usuários em que o comprimento fosse <= 4
Vamos ajustar um pouco para realismo.
(((36 ^ 7) / 1000000000) / 2) * 10000 = 2 dias
Supondo que não haja distinção entre maiúsculas e minúsculas, com um comprimento de senha <= 7, apenas caracteres alfanuméricos, levaria 4 dias para resolver para 10.000 registros de usuário, e reduzi pela metade a velocidade do algoritmo para refletir a sobrecarga e as circunstâncias não ideais.
É importante reconhecer que este é um ataque de força bruta linear, todos os cálculos são independentes um do outro, portanto é uma tarefa perfeita para vários sistemas resolverem. (IE fácil de configurar 2 computadores executando ataques de extremidades diferentes que ocupariam metade do tempo de execução).
Dado o caso de hash recursivo de uma senha 1.000 vezes para tornar esta tarefa mais cara computacionalmente:
(((36 ^ 7) / 1 000 000 000) / 2) * 1000 segundos = 10,8839117 horas
Isso representa um comprimento máximo de 7 caracteres alfanuméricos, a menos da metade da velocidade de execução do valor cotado para um usuário .
O hash recursivo 1.000 vezes bloqueia efetivamente um ataque geral, mas os ataques direcionados aos dados do usuário ainda são vulneráveis.
fonte
Respostas:
Sim, você precisa de apenas 3 dias para sha1 (salt | senha). É por isso que bons algoritmos de armazenamento de senha usam hashing de 1000 iterações: você precisará de 8 anos.
fonte
Isso não impede os ataques de dicionário.
O que isso faz é impedir que alguém que consegue uma cópia do seu arquivo de senha use uma tabela arco - íris para descobrir quais são as senhas dos hashes.
Eventualmente, pode ser uma força bruta, entretanto. A resposta a essa parte é forçar seus usuários a não usar palavras do dicionário como senhas (requisitos mínimos de pelo menos um número ou caractere especial, por exemplo).
Atualizar :
Eu deveria ter mencionado isso antes, mas alguns (a maioria?) Sistemas de senha usam um sal diferente para cada senha, provavelmente armazenado com a própria senha. Isso torna uma única tabela de arco-íris inútil. É assim que a biblioteca crypt do UNIX funciona, e sistemas operacionais modernos como o UNIX ampliaram essa biblioteca com novos algoritmos de hash.
Eu sei que o suporte para SHA-256 e SHA-512 foram adicionados em versões mais recentes do GNU crypt.
fonte
Para ser mais preciso, um ataque de dicionário , ou seja, um ataque em que todas as palavras de uma lista exaustiva são tentadas, não fica "impossível", mas torna-se impraticável : cada pedaço de sal dobra a quantidade de armazenamento e computação necessária .
Isso é diferente de ataques de dicionário pré-computados, como ataques envolvendo tabelas de arco-íris em que não importa se o sal é secreto ou não.
Exemplo: Com um salt de 64 bits (ou seja, 8 bytes), você precisa verificar 2 64 combinações de senha adicionais em seu ataque de dicionário. Com um dicionário contendo 200.000 palavras, você terá que fazer
testes no pior caso - em vez de 200.000 testes sem sal.
Um benefício adicional do uso de salt é que um invasor não pode pré-computar os hashes de senha de seu dicionário. Simplesmente levaria muito tempo e / ou espaço.
Atualizar
Sua atualização presume que um invasor já conhece o salt (ou o roubou). Claro que esta é uma situação diferente. Ainda assim, não é possível para o invasor usar uma tabela de arco-íris pré-computada. O que importa muito aqui é a velocidade da função de hashing. Para tornar um ataque impraticável, a função de hashing precisa ser lenta. MD5 ou SHA não são bons candidatos aqui porque foram projetados para serem rápidos; melhores candidatos para algoritmos de hash são Blowfish ou algumas variações dele.
Atualização 2
Uma boa leitura sobre como proteger os hashes de senha em geral (indo muito além da pergunta original, mas ainda interessante):
Corolário do artigo: Use hashes salgados criados com bcrypt (baseado no Blowfish) ou Eksblowfish que permite usar um tempo de configuração configurável para tornar o hash lento.
fonte
Um dicionário é uma estrutura onde os valores são indexados por chaves. No caso de um ataque de dicionário pré-calculado, cada chave é um hash e o valor correspondente é uma senha que resulta no hash. Com um dicionário pré-calculado em mãos, um invasor pode "instantaneamente" pesquisar uma senha que produzirá o hash necessário para fazer o login.
Com o salt, o espaço necessário para armazenar o dicionário cresce rapidamente ... tão rapidamente que tentar pré-computar um dicionário de senha logo se torna inútil.
Os melhores sais são escolhidos aleatoriamente de um gerador de números aleatórios criptográficos. Oito bytes é um tamanho prático e mais de 16 bytes não tem nenhum propósito.
O sal faz muito mais do que apenas "tornar o trabalho de um invasor mais irritante". Ele elimina toda uma classe de ataque - o uso de dicionários pré-computados.
Outro elemento é necessário para proteger completamente as senhas, que é o "reforço das chaves". Uma rodada de SHA-1 não é boa o suficiente: um algoritmo de hashing de senha segura deve ser muito lento em termos computacionais.
Muitas pessoas usam PBKDF2, uma função de derivação chave, que realimenta os resultados para a função hash milhares de vezes. O algoritmo "bcrypt" é semelhante, usando uma derivação de chave iterativa que é lenta.
Quando a operação de hashing é muito lenta, uma tabela pré-computada torna-se cada vez mais desejável para um invasor. Mas o sal adequado derrota essa abordagem.
Comentários
Abaixo estão os comentários que fiz sobre a questão.
Sem o salt, um invasor não usaria o método demonstrado na "Atualização 2". Ele simplesmente faria uma pesquisa em uma tabela pré-calculada e obteria a senha no tempo O (1) ou O (log n) (n sendo o número de senhas candidatas). O sal é o que impede isso e o força a usar a abordagem O (n) mostrada na "Atualização 2".
Uma vez reduzido a um ataque O (n), temos que considerar quanto tempo leva cada tentativa. O reforço de chave pode fazer com que cada tentativa no loop demore um segundo inteiro, o que significa que o tempo necessário para testar 10 mil senhas em 10 mil usuários vai se estender de 3 dias a 3 anos ... e com apenas 10 mil senhas, você provavelmente perderá senhas naquele tempo.
Você deve considerar que um invasor usará as ferramentas mais rápidas que puder, não o PHP, portanto, milhares de iterações, em vez de 100, seriam um bom parâmetro para reforço de chave. Deve levar uma grande fração de segundo para calcular o hash para uma única senha.
O reforço de chave é parte dos algoritmos de derivação de chave padrão PBKDF1 e PBKDF2, do PKCS # 5, que são ótimos algoritmos de ofuscação de senha (a "chave derivada" é o "hash").
Muitos usuários no StackOverflow referem-se a este artigo porque ele foi uma resposta à postagem de Jeff Atwood sobre os perigos das tabelas rainbow. Não é meu artigo favorito, mas discute esses conceitos com mais detalhes.
Claro que você assume que o invasor tem tudo: salt, hash, nome de usuário. Suponha que o invasor seja um funcionário corrupto da empresa de hospedagem que jogou a tabela do usuário no seu fansite myprettypony.com. Ele está tentando recuperar essas senhas porque vai se virar e ver se seus fãs de pôneis usaram a mesma senha em suas contas citibank.com.
Com um esquema de senha bem projetado, será impossível para esse cara recuperar qualquer senha.
fonte
O objetivo da salga é evitar a amortização do esforço do atacante.
Sem sal, uma única tabela de entradas de hash-senha pré-computadas (por exemplo, MD5 de todas as cadeias alfanuméricas de 5 caracteres, fácil de encontrar online) pode ser usada em todos os usuários em todos os bancos de dados do mundo.
Com um sal específico do site, o invasor precisa calcular a tabela sozinho e pode usá-la em todos os usuários do site.
Com um sal por usuário, o invasor precisa despender esse esforço para cada usuário separadamente.
Obviamente, isso não ajuda muito a proteger senhas realmente fracas direto de um dicionário, mas protege senhas razoavelmente fortes contra essa amortização.
fonte
Além disso - mais um ponto importante - usar um sal específico do USUÁRIO evita a detecção de dois usuários com a MESMA senha - seus hashes corresponderiam. É por isso que muitas vezes o hash é hash (salt + nome de usuário + senha)
Se você tentar manter o hash em segredo, o invasor também não poderá verificar os hashes.
Editar- apenas notei que o ponto principal foi feito em um comentário acima.
fonte
Os sais são implementados para evitar ataques de arco-íris. Uma rainbow table é uma lista de hashes pré-calculados, o que torna a tradução de um hash em sua frase muito mais simples. Você precisa entender que o sal não é eficaz como uma prevenção moderna para quebrar uma senha, a menos que tenhamos um algoritmo de hash moderno.
Digamos que estejamos trabalhando com SHA1, aproveitando os exploits recentes descobertos com este algoritmo, e digamos que temos um computador rodando a 1.000.000 hashes / segundo, levaria 5,3 milhões de milhões de anos para encontrar uma colisão , então sim php pode trabalhar 300 por segundo, grande uau, realmente não importa. A razão de salmos é porque se alguém se preocupou em gerar todas as frases comuns do dicionário, (2 ^ 160 pessoas, bem-vindos aos exploits da era de 2007).
Então aqui está um banco de dados real, com 2 usuários que uso para fins de teste e administração.
Na verdade, o esquema de sal é o seu sha1 (tempo de registro + nome de usuário). Vá em frente, diga-me minha senha, essas são senhas reais em produção. Você pode até sentar lá e ver uma lista de palavras em php. Enlouquecer.
Não sou louco, só sei que é seguro. Por diversão, a senha do teste é
test
.sha1(sha1(1281546174.065087 + test) + test) = 5872548f2abfef8cb729cac14bc979462798d023
Você precisaria gerar uma tabela de arco-íris inteira perpendida com
27662aee8eee1cb5ab4917b09bdba31d091ab732
apenas para este usuário. Isso significa que eu posso permitir que minhas senhas não sejam comprometidas por uma única tabela de arco-íris, o hacker precisa gerar uma tabela de arco-íris inteira para 27662aee8eee1cb5ab4917b09bdba31d091ab732 para teste e novamente f3f7735311217529f2e020468004a2aa5b3dee7f para briang. Pense nos 5,3 milhões de milhões de milhões de anos para todos os hashes. Pense no tamanho de armazenar apenas 2 ^ 80 hashes (isso é bem mais de 20 yottabytes ), isso não vai acontecer.Não confunda o sal com um meio de fazer um hash algo que você não pode decodificar, é um meio de evitar que uma rainbow table traduza todas as suas senhas de usuário. É impossível neste nível de tecnologia.
fonte
A ideia por trás do ataque de dicionário é que você pegue um hash e encontre a senha, a partir da qual esse hash foi calculado, sem cálculo de hash. Agora faça o mesmo com a senha com sal - você não pode.
Não usar um salt torna a pesquisa de senha tão fácil quanto a pesquisa no banco de dados. Adicionar um salt faz com que o invasor execute o cálculo hash de todas as senhas possíveis (mesmo para anexar um dicionário, isso aumenta significativamente o tempo de ataque).
fonte
Em termos mais simples: sem sal, cada senha candidata precisa apenas ser hash uma vez para compará-la com cada usuário, em qualquer lugar no "universo conhecido" (coleção de bancos de dados comprometidos), cuja senha é hash através do mesmo algoritmo. Com o sal, se o número de possíveis valores de sal exceder substancialmente o número de usuários no "universo conhecido", cada senha candidata deve ser hash separadamente para cada usuário contra o qual será testada.
fonte
Colocar simplesmente sal não impede que um hash ataque (força bruta ou dicionário), apenas torna mais difícil; o invasor precisará encontrar o algoritmo de sal (que, se implementado corretamente, fará uso de mais iterações) ou força bruta no algoritmo, o que, a menos que seja muito simples, é quase impossível. A salga também descarta quase completamente a opção de pesquisas da tabela arco-íris ...
fonte
O sal faz a mesa do arco-íris ataques à muito mais difíceis, pois torna um hash de senha única muito mais difícil de quebrar. Imagine que você tenha uma senha horrível de apenas o número 1. Um ataque de rainbow table quebraria isso imediatamente.
Agora imagine que cada senha no banco de dados é salgada com um valor longo e aleatório de muitos caracteres aleatórios. Agora sua senha ruim de "1" está armazenada no banco de dados como um hash de 1 mais um monte de caracteres aleatórios (o sal), portanto, neste exemplo, a tabela de arco-íris precisa ter o hash para algo como: 1.
Então, supondo que seu salt seja algo seguro e aleatório, digamos ()% ISLDGHASKLU ( % #% #, a tabela de arco-íris do hacker precisaria ter uma entrada para 1 * ()% ISLDGHASKLU (*% #% #. Agora usando uma tabela de arco-íris até mesmo essa senha simples não é mais prática.
fonte