Geração de sal e software de código aberto

Pelo que entendi, a melhor prática para gerar sais é usar alguma fórmula enigmática (ou até constante mágica) armazenada em seu código-fonte.

Eu estou trabalhando em um projeto que planejamos lançar como código aberto, mas o problema é que com a fonte vem a fórmula secreta para gerar sais e, portanto, a capacidade de executar ataques de tabela do arco-íris em nosso site.

Eu acho que muitas pessoas já contemplaram esse problema antes de mim, e estou me perguntando qual é a melhor prática. Parece-me que não faz sentido ter um sal se o código for de código aberto, porque os sais podem ser facilmente submetidos a engenharia reversa.

Pensamentos?

Realmente sais só precisam ser únicos para cada input. Mesmo que o invasor possa calcular o que é o sal, isso torna a tabela do arco-íris extremamente difícil de criar. Isso ocorre porque o salt é adicionado à senha antes de ser hash, portanto, ela é efetivamente adicionada ao número total de inputs que a tabela rainbow deve conter para ter uma lista de todos os valores possíveis para um campo de senha.

Já que as perguntas sobre salgadinhos aparecem regularmente e parece haver alguma confusão sobre o assunto, eu estendi essa resposta.

O que é um sal?

Um salt é um conjunto random de bytes de um comprimento fixo que é adicionado à input de um algoritmo hash.

Por que salgar (ou semear) é útil um hash?

Adicionar um sal random a um hash garante que a mesma senha produza muitos hashes diferentes. O sal geralmente é armazenado no database, juntamente com o resultado da function hash. Salgar um hash é bom por vários motivos:

  1. Salgar aumenta muito a dificuldade / custo de ataques pré-computados (incluindo mesas arco-íris )
  2. Salgar garante que a mesma senha não resulte no mesmo hash. Isso garante que você não possa determinar se dois usuários têm a mesma senha. E, o que é ainda mais importante , você não pode determinar se a mesma pessoa usa a mesma senha em diferentes sistemas.
  3. A salga aumenta a complexidade das senhas, diminuindo, assim, a eficácia dos ataques de dictionary e aniversário . (Isso só é verdade se o sal for armazenado separado do hash).
  4. A salga adequada aumenta muito a necessidade de armazenamento para ataques de pré-computação, até o ponto em que eles não são mais práticos. (Senhas alfanuméricas com diferenciação de maiúsculas e minúsculas de 8 caracteres com salt de 16 bits, com hash até um valor de 128 bits, ocupariam pouco menos de 200 exabytes sem redução do arco-íris).

Não há necessidade de o sal ser secreto.

Um salt não é uma chave secreta, em vez disso, um salt ‘funciona’, tornando a function hash específica para cada instância. Com hash salgado, não há uma function hash, mas uma para cada valor de sal possível. Isso impede que o invasor atue com N senhas com hash por menos de N vezes o custo de atacar uma senha. Este é o ponto do sal.
Um “sal secreto” não é um sal, é chamado de “chave”, e significa que você não está mais computando um hash, mas um Message Authentication Code (MAC). Computing MAC é um negócio complicado (muito mais complicado do que simplesmente juntar uma chave e um valor em uma function hash) e é um assunto completamente diferente.

O sal deve ser random para cada instância em que é usado. Isso garante que um invasor tenha que atacar cada hash salgado separadamente.
Se você confia em seu sal (ou algoritmo de salga) sendo secreto, você entra nos domínios da Segurança através da Obscuridade (não funciona). Muito provavelmente, você não recebe segurança adicional do sigilo do sal; você só tem a sensação de segurança. Então, ao invés de tornar seu sistema mais seguro, isso apenas o distrai da realidade.

Então, por que o sal tem que ser random?

Tecnicamente, o sal deve ser único . O ponto do sal deve ser distinto para cada senha com hash. Isso se entende em todo o mundo . Como não existe uma organização central que distribua sais únicos sob demanda, temos que confiar na próxima melhor opção, que é a seleção aleatória com um gerador random imprevisível, preferencialmente dentro de um espaço salgado grande o suficiente para tornar as colisões improváveis ​​(duas instâncias usando o mesmo valor salino).

É tentador tentar extrair um sal a partir de alguns dados que são “presumivelmente únicos”, como o ID do usuário, mas esses esquemas geralmente falham devido a alguns detalhes desagradáveis:

  1. Se você usar, por exemplo, o ID do usuário , alguns criminosos, atacando sistemas distintos, poderão unir seus resources e criar tabelas pré-computadas para os IDs de usuário 1 a 50. Um ID de usuário é exclusivo em todo o sistema, mas não em todo o mundo .

  2. O mesmo se aplica ao nome de usuário : existe uma “raiz” por sistema Unix, mas existem muitas raízes no mundo. Uma tabela de arco-íris para “raiz” valeria a pena, já que poderia ser aplicada a milhões de sistemas. Pior ainda, há também muitos “bob” por aí, e muitos não têm treinamento em sysadmin: suas senhas podem ser bem fracas.

  3. A singularidade também é temporal. Às vezes, os usuários mudam sua senha. Para cada nova senha , um novo salt deve ser selecionado. Caso contrário, um invasor obteria o hash da senha antiga e o hash do novo poderia tentar atacar os dois simultaneamente.

Usar um sal random obtido a partir de um PRNG criptograficamente seguro e imprevisível pode ser algum tipo de exagero, mas pelo menos provavelmente protege você contra todos esses riscos. Não se trata de impedir que o invasor saiba o que é um sal individual , mas sim de não dar a ele o alvo grande e gordo que será usado em um número substancial de alvos em potencial. A seleção aleatória torna os alvos tão finos quanto é prático.

Em conclusão:

Use um sal de alta entropia aleatoriamente distribuído e uniformemente distribuído. Use um novo sal sempre que você criar uma nova senha ou alterar uma senha. Armazene o sal junto com a senha com hash. Favor sais grandes (pelo menos 10 bytes, preferivelmente 16 ou mais).

Um sal não transforma uma senha incorreta em uma boa senha. Apenas garante que o atacante pagará pelo menos o preço de ataque do dictionary por cada senha incorreta que quebrar.

Fontes úteis:
stackoverflow.com: Sal não random para hashes de senha
Bruce Schneier: Criptografia Prática (livro)
Segurança Matasano: Chega de Tabelas Arco-Íris
usenix.org: Cripta Unix usa sal desde 1976
owasp.org : Por que adicionar sal
openwall.com : sais

Aviso Legal:
Eu não sou especialista em segurança. (Embora esta resposta foi revista por Thomas Pornin )
Se algum dos profissionais de segurança encontrar algo errado, por favor, comente ou edite esta resposta da wiki.

Como o Unix se tornou popular, o caminho certo para armazenar uma senha foi acrescentar um valor random (o sal) e misturá-lo. Salve o sal fora de onde você pode chegar mais tarde, mas onde você espera que os bandidos não consigam.

Isso tem alguns bons efeitos. Primeiro, os bandidos não podem apenas fazer uma lista de senhas esperadas como “Password1”, transformá-las em uma tabela de arco-íris e passar pelo seu arquivo de senhas procurando por correspondências. Se você tem um bom sal de dois bytes, eles precisam gerar 65.536 valores para cada senha esperada, o que torna a tabela de arco-íris muito menos prática. Segundo, se você pode manter o sal dos caras mal-intencionados que estão olhando para o seu arquivo de senhas, você tornou muito mais difícil calcular possíveis valores. Terceiro, você tornou impossível para os bandidos determinar se uma determinada pessoa usa a mesma senha em sites diferentes.

Para fazer isso, você gera um sal random. Isso deve gerar todos os números no intervalo desejado com probabilidade uniforme. Isso não é difícil; um simples gerador linear de números randoms congruentes se sairá bem.

Se você tem cálculos complicados para fazer o sal, está fazendo errado. Se você calcular com base na senha, você está fazendo errado. Nesse caso, tudo o que você está fazendo é complicar o hash e não adicionar funcionalmente nenhum sal.

Ninguém bom em segurança contaria em esconder um algoritmo. A criptografia moderna é baseada em algoritmos que foram extensivamente testados e, para serem extensivamente testados, precisam ser bem conhecidos. Geralmente, descobriu-se ser mais seguro usar algoritmos padrão em vez de rolar os próprios e esperar que seja bom. Não importa se o código é de código aberto ou não, ainda é possível que os criminosos analisem o que um programa faz.

Você pode apenas gerar um sal random para cada registro em tempo de execução. Por exemplo, digamos que você esteja armazenando senhas de usuário com hash em um database. Você pode gerar uma string aleatória de oito caracteres de caracteres alfanuméricos maiúsculos e minúsculos em tempo de execução, preceder à senha, agrupar essa sequência e armazená-la no database. Uma vez que existem 62 8 sais possíveis, gerar tabelas de arco-íris (para cada sal possível) será proibitivamente caro; e como você está usando um salt exclusivo para cada registro de senha, mesmo que um invasor tenha gerado algumas tabelas de arco-íris correspondentes, ele ainda não conseguirá quebrar todas as senhas.

Você pode alterar os parâmetros de sua geração de sal com base em suas necessidades de segurança; por exemplo, você pode usar um sal mais longo ou gerar uma string aleatória que também contenha sinais de pontuação para aumentar o número de possíveis sais.

Use um gerador de function aleatória para gerar o salt e armazene-o no database, faça um sal por linha e armazene-o no database.

Eu gosto de como o sal é gerado no registro do django. Referência: http://bitbucket.org/ubernostrum/django-registration/src/tip/registration/models.py#cl-85

salt = sha_constructor(str(random.random())).hexdigest()[:5] activation_key = sha_constructor(salt+user.username).hexdigest() return self.create(user=user, activation_key=activation_key) 

Ele usa uma combinação de sha gerada por um número random e o nome de usuário para gerar um hash.

Sha si é bem conhecido por ser forte e inquebrável. Adicione várias dimensões para gerar o sal em si, com o número random, sha e o componente específico do usuário, você tem segurança inquebrável!

No caso de um aplicativo de desktop que criptografa dados e os envia em um servidor remoto, como você considera o uso de um sal diferente a cada vez?

Usando o PKCS # 5 com a senha do usuário, é necessário um gerador para gerar uma chave de criptografia, para criptografar os dados. Eu sei que manter o sal codificado (ofuscado) no aplicativo de desktop não é uma boa ideia.

Se o servidor remoto NUNCA deve saber a senha do usuário, é possível usar sal diferente a cada vez? Se o usuário usar o aplicativo da área de trabalho em outro computador, como ele poderá descriptografar os dados no servidor remoto se ele não tiver a chave (não está codificado no software)?