Lunes Apps

Criptografia: Como funciona | pt.3

Este é o terceiro e último artigo da série de Criptografia. Vamos ensinar o essencial da parte técnica para toda a comunidade entender como funciona.

Funções de hash ruins para senhas

Tudo o mais, em especial soluções caseiras que as pessoas insistem em criar, assumindo que “criptografia segura” é “jogar fora toda operação criptográfica ou não que pode ser pensada”. Veja esta pergunta como exemplo. O princípio nela parece ser de que a complexidade com a bagunça das instruções vai confundir atacantes. Na prática, o desenvolvedor vai sempre estar mais perdido na sua própria criação do que o atacante.

 

Complexidade é ruim. Solução caseira é ruim. Novidade é ruim. Lembrando disso você vai evitar 99% dos problemas de hash de senhas, e segurança em geral.

 

Hash de senha no Windows costumava ser terrivelmente medonho, agora é só péssimo (MD4 sem salt e iterações).

 

Derivação de chave

Até agora nós consideramos a questão de hash das senhas. Um problema próximo é a transformação de uma senha numa chave simétrica que possa ser usada para criptografia. Isto é chamado de derivação de chave e é a primeira coisa que você faz quando encripta um arquivo com senha.

 

É possível fazer exemplos elaborados de funções de hash que são seguras para guardar um tokende validação, mas que são péssimas para gerar chaves simétricas; o oposto é possível, da mesma maneira. Esses exemplos entretanto são artificiais. Para casos práticos como os descritos acima:

 

  • A saída de uma hash de senha é aceitável como chave simétrica, após ser truncada no tamanho requerido.
  • Uma função de derivação de chave pode servir como hash de senha, desde que a chave derivada seja longa o suficiente pra evitar “pré-imagens genéricas” (o atacante tem a sorte de achar uma senha que dê o mesmo resultado). Uma saída de 100 bits deve ser suficiente.

 

Para falar a verdade, PBKDF2 e scrypt são funções de derivação de chave, e não de hashing — e o NIST aprova o PBKDF2 como função de derivação de chave, e não explicitamente como hasher. (mas com um pouquinho só de hipocrisia, dá pra ler o material do NIST de maneira a se entender que o PBKDF2 é bom para hash de senhas).

 

Ainda, o bcrypt é na verdade um block cipher (a parte de processamento de senha é o “key schedule”) que é depois usado em modo CTR para produzir três blocos de 192 bits de saída pseudo-aleatória, fazendo-o uma espécie de função de hash. O bcrypt pode virar uma função de derivação de chave com uma leve operação, usando o block cypher em modo CTR para gerar mais blocos. Mas, como de costume, não recomentamos remendos caseiros. Felizmente 192 bits são mais que suficientes para a maioria dos propósitos (por exemplo, encriptação simétrica com GCM ou EAX só precisa de 128 bits na chave).

 

Considerações adicionais

 

Quantas iterações?

Quanto mais melhor! Essa corrida de lento-e-salted é uma disputa acirrada entre atacante e defensor. Você usa muitas iterações para deixar o hashing mais dificil para todos. Para aumentar a segurança, você deve deixar o número mais alto que seja tolerado no servidor, considerando as outras coisas que ele deve fazer. Quanto mais alto melhor.

 

Colisões e MD5

O MD5 está quebrado: é muito fácil conseguir vários pares de entradas diferentes com o mesmo valor de saída. Estas são as colisões. Entretanto, colisões não são um problema para hash de senhas. Hash de senhas tem que resistir às pré-imagens, não colisões. Colisões são pares que dão mesma saída sem restrições, enquanto no hash de senhas o atacante tem que achar uma mensagem que dê uma determinada saída, que ele não escolheu. Isso é bem diferente. Até onde sabemos, o MD5 é praticamente tão forte quanto sempre foi em relação a pré-imagens (há um ataque teórico que ainda está muito distante de ser viável na prática).

 

O verdadeiro problema com o MD5 é que seu uso é muito comum para senhas, ele é muito rápido e não tem salt. Mas o PBKDF2 usado com MD5 seria robusto. Você deve usar SHA-1 ou SHA-256, mas por questão de “relações públicas”. As pessoas ficam nervosas quando ouvem “MD5”.

 

Geração do salt

O objetivo fundamental do salt é ser o mais único possível. Sempre que um salt é reusado, potencialmente pode ajudar a um atacante. Por exemplo, se você usa o nome do usuário como salt, um atacante pode construir rainbow tables usando “admin” e/ou “root”, pois a tabela serviria para muitos lugares que possuem usuários com esses “nomes”.

 

Da mesma forma, quando um usuário muda a senha, o nome permanece, levando ao reúso do salt. Senhas velhas são sempre alvos de valor, pois usuários tendem a reaproveitá-las em muitos lugares. (é sempre divulgado que é péssima idéia, todo mundo sabe, mas continua fazendo porque torna a vida mais fácil), e além disso as pessoas tendem a gerar senhas sequenciais. Se a senha velha do Alaor é “SenhaSecreta37”, bem capaz que a nova seja “SenhaSecreta38” ou “SenhaSecreta39”.

 

O jeito “barato” de obter salts únicos é usar aleatoriedade. Se você gera seu salt com bytes aleatórios de um gerador seguro que seu OS ofereça, (/dev/urandom, CryptGenRandom()…) você terá valores de salt “únicos suficientemente”, se forem de 16 bytes por exemplo, para nunca ver na vida uma colisão de salt.

 

O UUID é um jeito padrão de se obter valores “únicos”. Lembrando que UUID “versão 4” usa aleatoriedade (122 bits), como mencionado acima. Vários frameworks oferecem funções simples para gerar UUID sob demanda, e eles podem ser usados como salt.

 

O salt tem que ser secreto?

O salt não foi feito pra ser secreto, senão seria chamado de chave. Você não precisa divulgar o salt, mas se precisar (usando uma solução de hash no cliente, por exemplo), não se preocupe. O salt existe apenas para ser único. Não é nada mais do que a seleção de uma função de hash entre muitas.

 

Pepper

Criptógrafos não podem deixar uma metáfora quieta. Eles precisam estendê-las com mais analogias e trocadilhos (salt significa “sal”, pepper significa “pimenta”). Se você usa uma pepperna sua função de hash, está usando uma espécie diferente de algoritmo criptográfico; você está calculando um código de autenticação de mensagem (MAC) sobre a senha. A chave do MAC é sua pepper.

 

“Apimentar” faz sentido se você tiver uma chave secreta que o atacante não é capaz de ler. Lembre-se de que nós usamos o hash por que consideramos que um atacante pode conseguir uma cópia da base de dados do servidor ou até o disco todo. Um caso comum é um servidor com dois discos em RAID 1. Um disco falha, por ter a placa queimada. Acontece a toda hora. O sysadmin troca o disco, o espelho é refeito, e nada é perdido graças à magica do RAID 1. Bom, mas o disco velho não funciona mais, e o sysadmin não tem mais uma maneira fácil de limpar seu conteúdo, então ele simplesmente descarta o disco. O atacante encontra o disco no lixo, troca a placa e, vejam só! Ele tem uma cópia completa do sistema, com base de dados, configuração, executáveis, OS…

 

Para a pepper funcionar num caso desses, você precisa de algo mais que um PC com discos; você precisa de um dispositivo de segurança por hardware (HSM). Os HSMs são caros, tanto em termos econômicos quanto operacionais, mas com um HSM, basta usar a pepper e processar as senhas com um simples HMAC (por exemplo com SHA-1 ou SHA-256). Isso vai ser muito mais eficiente que bcrypt/PBKDF2/scrypt e suas complexas iterações. Fora que usar um HSM fica com cara bem profissional numa WebTrust audit.

 

Hashing do lado do cliente

Como o hashing é propositalmente “caro”, faria sentido numa arquitetura cliente-servidor usar a CPU do cliente. Afinal, quando 100 clientes conectam a um servidor só, coletivamente eles têm muito mais poder de processamento. Para isto, o server precisa mandar o salt para o cliente. Isso implica em mais uma ida e volta de dados, o que pode ou não ser fácil de adicionar em casos específicos. Num contexto web é mais complicado, pois o javascript sempre foi mais “anêmico” pra usar a CPU. Num contexto de senha segura remota (SRP), o hashing tem que acontecer de qualquer forma no lado do cliente.

 

Conclusão

Use bcrypt (há quem seja contra). PBKDF2 também não é ruim. Caso você use scrypt, vai estar “ligeiramente adiantado”, com os riscos implícitos nessa expressão; mas é bom pro progresso da ciência. Ser um “crash dummy” é uma profissão honrada!

 

Original text under license Creative Commons ShareAlike

 


 

 

Junte-se às nossas mídias sociais:

Facebook: https://goo.gl/4J5HDY

Twitter: https://goo.gl/a4o2G7

Telegram News: https://t.me/LunesNews

Telegram Inglês: https://goo.gl/1vjkDr

Telegram Espanhol: https://t.me/EspanolLunes

Telegram Francês: https://t.me/LunesFrancais

Telegram Português: https://goo.gl/y1qZfj

Discord: https://discord.gg/2zpywNW



Relacionados