Práticas recomendadas: senhas salgadas e salgadas?


145

Me deparei com uma discussão na qual aprendi que o que eu estava fazendo não era salgar senhas, mas salpicá-las, e desde então comecei a fazer as duas coisas com uma função como:

hash_function($salt.hash_function($pepper.$password)) [multiple iterations]

Ignorando o algoritmo de hash escolhido (quero que seja uma discussão sobre sais e pimentas e não algoritmos específicos, mas estou usando um seguro), essa é uma opção segura ou devo fazer algo diferente? Para aqueles que não estão familiarizados com os termos:

  • Um salt é um valor gerado aleatoriamente, geralmente armazenado com a sequência no banco de dados projetada para tornar impossível o uso de tabelas de hash para quebrar senhas. Como cada senha tem seu próprio sal, todas elas devem ser forçadas brutalmente individualmente para quebrá-las; no entanto, como o salt é armazenado no banco de dados com o hash da senha, um comprometimento do banco de dados significa perder os dois.

  • Uma pimenta é um valor estático em todo o site armazenado separadamente do banco de dados (geralmente codificado no código fonte do aplicativo) que se destina a ser secreto. É usado para que um comprometimento do banco de dados não faça com que a tabela de senhas do aplicativo inteiro seja brutalmente forçada.

Falta alguma coisa e salgar e digitar minhas senhas é a melhor opção para proteger a segurança do usuário? Existe alguma falha de segurança em potencial dessa maneira?

Nota: Suponha que, para os objetivos da discussão, o aplicativo e o banco de dados sejam armazenados em máquinas separadas, não compartilhem senhas etc., portanto, uma violação do servidor de banco de dados não significa automaticamente uma violação do servidor de aplicativos.


3
Não é exatamente uma duplicata, mas é extremamente relacionado: stackoverflow.com/questions/16594613/…
ircmaxell

2
Duplicação entre sites: security.stackexchange.com/q/3272/2113
Jacco

Respostas:


306

Está bem. Vendo como eu preciso escrever sobre este longo e mais , eu vou fazer uma última resposta canônica na pimenta sozinho.

A aparente parte superior das pimentas

Parece bastante óbvio que os pimentões devem tornar as funções de hash mais seguras. Quero dizer, se o invasor apenas obtém seu banco de dados, as senhas dos usuários devem ser seguras, certo? Parece lógico, certo?

É por isso que tantas pessoas acreditam que o pimentão é uma boa ideia. Faz sentido".

A realidade das pimentas

Nos domínios de segurança e criptografia, "faz sentido" não é suficiente. Algo tem que ser comprovável e fazer sentido para ser considerado seguro. Além disso, ele deve ser implementável de maneira sustentável. O sistema mais seguro que não pode ser mantido é considerado inseguro (porque, se alguma parte dessa segurança falha, o sistema inteiro desmorona).

E os pimentões não se encaixam nem nos modelos prováveis ​​nem sustentáveis ​​...

Problemas teóricos com pimentas

Agora que preparamos o cenário, vejamos o que há de errado com os pimentões.

  • Alimentar um hash em outro pode ser perigoso.

    No seu exemplo, você faz hash_function($salt . hash_function($pepper . $password)).

    Sabemos por experiências anteriores que "apenas alimentar" um resultado de hash em outra função de hash pode diminuir a segurança geral. O motivo é que ambas as funções de hash podem se tornar um alvo de ataque.

    É por isso que algoritmos como PBKDF2 usam operações especiais para combiná-los (nesse caso, o hmac).

    O ponto é que, embora não seja grande coisa, também não é uma coisa trivial apenas dar uma volta. Os sistemas de criptografia são projetados para evitar casos "devem funcionar" e, em vez disso, se concentram nos casos "projetados para funcionar".

    Embora isso possa parecer puramente teórico, na verdade não é. Por exemplo, o Bcrypt não pode aceitar senhas arbitrárias . Portanto, a passagem bcrypt(hash(pw), salt)pode realmente resultar em um hash muito mais fraco do que bcrypt(pw, salt)se hash()retorna uma string binária.

  • Trabalhando contra o design

    A maneira como o bcrypt (e outros algoritmos de hash de senha) foram projetados é trabalhar com um sal. O conceito de pimenta nunca foi introduzido. Isso pode parecer uma trivialidade, mas não é. A razão é que um sal não é um segredo. É apenas um valor que pode ser conhecido por um invasor. Uma pimenta, por outro lado, por definição, é um segredo criptográfico.

    Os algoritmos atuais de hash de senha (bcrypt, pbkdf2, etc) são todos projetados para receber apenas um valor secreto (a senha). Adicionar outro segredo ao algoritmo ainda não foi estudado.

    Isso não significa que não é seguro. Isso significa que não sabemos se é seguro. E a recomendação geral com segurança e criptografia é que, se não sabemos, não é.

    Portanto, até que os algoritmos sejam projetados e examinados pelos criptografadores para uso com valores secretos (pimentas), os algoritmos atuais não devem ser usados ​​com eles.

  • A complexidade é o inimigo da segurança

    Acredite ou não, a complexidade é o inimigo da segurança . Criar um algoritmo que pareça complexo pode ser seguro ou não. Mas as chances são bastante significativas de que não é seguro.

Problemas significativos com pimentas

  • Não é de manutenção

    Sua implementação de pimentas impede a capacidade de girar a chave de pimenta. Como a pimenta é usada na entrada para a função unidirecional, você nunca pode alterá-la durante a vida útil do valor. Isso significa que você precisaria criar alguns hacks instáveis ​​para suportar a rotação de teclas.

    Isso é extremamente importante, pois é necessário sempre que você armazena segredos criptográficos. Não ter um mecanismo para girar as chaves (periodicamente e após uma violação) é uma enorme vulnerabilidade de segurança.

    E sua abordagem atual do Pepper exigiria que todos os usuários tivessem sua senha completamente invalidada por uma rotação ou esperassem até o próximo login para girar (o que pode nunca acontecer) ...

    O que basicamente torna sua abordagem um impedimento imediato.

  • Requer que você role sua própria criptografia

    Como nenhum algoritmo atual suporta o conceito de pimenta, é necessário compor algoritmos ou inventar novos para suportar uma pimenta. E se você não pode ver imediatamente por que isso é realmente ruim:

    Qualquer pessoa, do amador mais ignorante ao melhor criptógrafo, pode criar um algoritmo que ele próprio não pode quebrar.

    NUNCA role sua própria criptografia ...

A melhor maneira

Portanto, dentre todos os problemas detalhados acima, há duas maneiras de lidar com a situação.

  • Basta usar os algoritmos como eles existem

    Se você usar bcrypt ou scrypt corretamente (com um alto custo), todas as senhas mais fracas do dicionário deverão ser estatisticamente seguras. O registro atual de hash de criptografia a custo 5 é de 71k hashes por segundo. Nesse ritmo, mesmo uma senha aleatória de 6 caracteres levaria anos para quebrar. E considerando que meu custo mínimo recomendado é 10, isso reduz os hashes por segundo em um fator de 32. Portanto, estaríamos falando apenas de 2200 hashes por segundo. Nesse ritmo, até mesmo algumas frases ou modificações de dicionário podem ser seguras.

    Além disso, devemos verificar essas classes fracas de senhas na porta e não permitir que elas entrem. À medida que a quebra de senhas se torna mais avançada, os requisitos de qualidade das senhas também devem ocorrer. Ainda é um jogo estatístico, mas com uma técnica de armazenamento adequada e senhas fortes, todos devem estar praticamente muito seguros ...

  • Criptografar o hash de saída antes do armazenamento

    Existe no domínio da segurança um algoritmo projetado para lidar com tudo o que dissemos acima. É uma cifra de bloco. É bom, porque é reversível, para que possamos girar as teclas (yay! Manutenibilidade!). É bom porque está sendo usado como projetado. É bom porque não fornece informações ao usuário.

    Vamos olhar para essa linha novamente. Digamos que um invasor conhece seu algoritmo (que é necessário para segurança, caso contrário, é segurança através da obscuridade). Com uma abordagem tradicional de pimenta, o atacante pode criar uma senha de sentinela e, como conhece o sal e a saída, pode forçar a pimenta com força bruta. Ok, isso é um tiro no escuro, mas é possível. Com uma cifra, o atacante não recebe nada. E como o sal é randomizado, uma senha de sentinela nem sequer o ajudará. Portanto, o melhor que resta é atacar o formulário criptografado. O que significa que eles primeiro precisam atacar seu hash criptografado para recuperar a chave de criptografia e depois atacar os hashes. Mas há muita pesquisa sobre o ataque de cifras, então queremos confiar nisso.

TL / DR

Não use pimentas. Existem vários problemas com eles e existem duas maneiras melhores: não usar nenhum segredo do lado do servidor (sim, tudo bem) e criptografar o hash de saída usando uma cifra de bloco antes do armazenamento.


6
Obrigado por incluir a última parte da criptografia do valor do hash, esta é uma resposta com a qual concordo plenamente. Se a criptografia se tornaria parte de sua api senha , não haveria nenhuma razão para não usá-lo, então talvez ... (eu gostaria de escrever a documentação para ele)
martinstoeckli

2
@martinstoeckli: eu não concordaria em adicionar a etapa de criptografia à API de hash simplificada. O motivo é que o armazenamento de segredos (chaves) é muito mais difícil do que as pessoas imaginam, e é muito fácil dar um tiro no próprio pé. Para 99,9% dos usuários lá fora, bcrypt cru é mais do que suficiente para todos, mas as senhas mais simples ...
ircmaxell

7
@ircmaxell - Por outro lado, você não perderia nada. Na pior das hipóteses, quando a chave se torna conhecida, um invasor ainda deve decifrar o hash BCrypt (mesma situação que sem criptografia). Não é o mesmo que armazenar uma chave para criptografar dados; trata-se de adicionar um segredo do lado do servidor. Mesmo uma chave codificada protegeria essas senhas fracas, desde que o invasor não tenha controle sobre o servidor / código. Essa situação não é incomum: além da injeção de SQL, também jogamos fora os backups, os servidores descartados ... podem levar a essa situação. Muitos usuários de PHP trabalham em servidores hospedados.
martinstoeckli

2
@martinstoeckli Eu teria que concordar com o ircmaxwell aqui, não acho que a criptografia pertença à API da senha. Entretanto, pode-se observar que, para a segurança máxima, a criptografia de seus hashes é uma provisão adicional que pode ser usada.
Foochow

Eu tenho uma pergunta sobre a criptografia da saída antes do armazenamento. Se não me engano, as chaves de criptografia geralmente são usadas para criptografar mensagens de forma exclusiva ou transitória. Se, no entanto, você tiver 200.000 senhas de usuário, todas criptografadas com a mesma chave e armazenadas juntas, isso não facilita o ataque à chave? Você tem agora 200.000 mensagens, em comparação com apenas 1 ou 2.
AgmLauncher

24

Primeiro devemos falar sobre a vantagem exata de uma pimenta :

  • O pepper pode proteger senhas fracas de um ataque de dicionário, no caso especial, em que o invasor tem acesso de leitura ao banco de dados (contendo os hashes), mas não tem acesso ao código-fonte com o pepper.

Um cenário típico seria a injeção de SQL, backups descartados, servidores descartados ... Essas situações não são tão incomuns quanto parecem e geralmente não estão sob seu controle (hospedagem de servidor). Se você usar...

  • Um sal único por senha
  • Um algoritmo de hash lento como o BCrypt

... senhas fortes estão bem protegidas. É quase impossível forçar com força bruta uma senha forte nessas condições, mesmo quando o sal é conhecido. O problema são as senhas fracas, que fazem parte de um dicionário de força bruta ou são derivações delas. Um ataque de dicionário revelará esses muito rapidamente, porque você testa apenas as senhas mais comuns.

A segunda pergunta é como aplicar a pimenta ?

Uma maneira frequentemente recomendada de aplicar uma pimenta é combinar a senha e a pimenta antes de passá-la para a função hash:

$pepperedPassword = hash_hmac('sha512', $password, $pepper);
$passwordHash = bcrypt($pepperedPassword);

Existe outra maneira ainda melhor:

$passwordHash = bcrypt($password);
$encryptedHash = encrypt($passwordHash, $serverSideKey);

Isso não apenas permite adicionar um segredo do lado do servidor, mas também permite trocar o $ serverSideKey, caso isso seja necessário. Este método envolve um pouco mais de trabalho, mas se o código existir uma vez (biblioteca), não há razão para não usá-lo.


Então você diria que uma pimenta acrescenta segurança sobre apenas um sal, em suma? Obrigado pela ajuda sobre como implementar.
Glitch Desejo

1
@LightningDust - Sim, para senhas fracas, desde que a pimenta permaneça secreta. Atenua alguns tipos bem definidos de ameaças.
martinstoeckli

@martinstoeckli definitivamente é uma boa maneira de implementar isso. É bom ver que alguém com alguma experiência em segurança suporta esse método. Uma AES_ENCRYPT($passwordHash, $serverSideKey)chamada do MySQL também seria uma maneira apropriada de implementar isso?
Foochow

1
@Foo_Chow - Não sei a implementação da função MySQL, mas parece que eles usaram o modo EBC, para evitar o vetor IV. Juntamente com o texto simples conhecido (valores de hash sempre começam com os mesmos caracteres), isso pode ser um problema. Na minha página inicial, publiquei um exemplo de implementação, que lida com essa criptografia.
martinstoeckli

@martinstoeckli interessante, não muito familiarizado com os conceitos; no entanto, parece que um IV seria desejável para os resultados mais robustos. Parece não adicionar muita sobrecarga para o benefício adicionado.
Foochow

2

O objetivo do sal e da pimenta é aumentar o custo de uma pesquisa de senha pré-calculada, chamada tabela arco-íris.

Em geral, tentar encontrar uma colisão para um único hash é difícil (supondo que o hash esteja seguro). No entanto, com hashes curtos, é possível usar o computador para gerar todos os hashes possíveis em uma pesquisa em um disco rígido. Isso é chamado de tabela arco-íris. Se você criar uma tabela arco-íris, poderá sair para o mundo e encontrar rapidamente senhas plausíveis para qualquer hash (sem sal e sem sal).

O ponto principal é tornar a tabela arco-íris necessária para hackear sua lista de senhas exclusiva. Assim, desperdiçando mais tempo no invasor para construir a tabela do arco-íris.

O objetivo do jogo, no entanto, é fazer com que a tabela do arco-íris de cada usuário seja exclusiva do usuário, aumentando ainda mais a complexidade do ataque.

Realmente, o objetivo da segurança do computador quase nunca é torná-lo (matematicamente) impossível, apenas matematicamente e fisicamente impraticável (por exemplo, em sistemas seguros, seria necessária toda a entropia no universo (e mais) para calcular a senha de um único usuário).


Então, um sal + pimenta oferece mais segurança do que apenas um sal? Ou seria melhor largar a pimenta e executar mais iterações de scrypt?
Glitch Desejo

2
O principal sobre uma tabela arco-íris é que você não cria uma para um ataque específico, faz o download de uma já existente. Existem longas tabelas arco-íris disponíveis para algoritmos de hash populares a apenas um google de distância!
Richard

1
@LightningDust Eu não consigo pensar em nenhum motivo. No entanto, Richard no outro segmento veio com um. Você pode ocultar a pimenta no seu código-fonte, o que significa outro lugar ao qual o invasor precisa ter acesso, apenas para montar uma tabela de arco-íris.
Aron #

@Aron - Bem, foi o que pensei, já que temos servidores de aplicativos separados dos servidores de banco de dados (ou seja, se você entrar rootno nosso servidor db, você ainda não tem acesso ao nosso servidor de aplicativos), que esconde pimenta no código-fonte (em nosso arquivo de configuração) forneceria segurança adicional.
Glitch Desejo

1

Não é possível que o armazenamento de um valor codificado no código fonte tenha alguma relevância para a segurança. É segurança através da obscuridade.

Se um hacker adquirir seu banco de dados, ele poderá começar a forçar o uso forçado de suas senhas de usuário. Não vai demorar muito para que o hacker identifique sua pimenta se ele conseguir quebrar algumas senhas.


1
Isso tornaria inútil uma tabela arco-íris pré-computada para uma tabela de senha sem sal. Em suma, mesmo que o atacante soubesse sua pimenta, ele precisaria criar uma nova tabela de arco-íris.
Aron

3
Ele fortalece o hash com base em dados não disponíveis no banco de dados, o que é uma coisa boa. As coisas no banco de dados podem ser potencialmente reveladas em vulnerabilidades - é menos provável que um valor no código seja acessado da mesma maneira.
Richard

Os sais são funções unidirecionais, mesmo que forçar brutalmente uma senha apenas lhe daria essa senha e não o ajudaria a obter o valor da pimenta em si.
Glitch Desire

1
@LightningDust Acho que você quer dizer "hashes são funções de alçapão". Sim, é impossível descobrir o seu sal e / ou pimenta a partir de um hash seguro (na verdade, essa é a definição de um hash seguro).
Aron

6
O @Aron Security Through Obscurity pode ser uma técnica válida usada como uma camada de defesa. O ponto é que nunca deve ser invocado como uma defesa, mas usado para "desacelerar o atacante". Se não fosse esse o caso, algo como um honeypot não seria usado. Em vez disso, podemos efetivamente usar a segurança através da obscuridade para ajudar a retardar os invasores, desde que não confiemos nela para a segurança de nosso aplicativo.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.