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.