PHP Conference Nagoya 2025

Gerando hash de senhas de forma segura

Esta seção explica as razões por trás do uso de funções de hash para proteger senhas, bem como fazê-lo de forma eficaz.

Por que deve ser feito hash das senhas fornecidas pelos usuários?

Gerar hash de senha é uma das considerações mais básicas de segurança que deve ser feita ao projetar qualquer aplicação ou serviço que aceite senhas dos usuários. Sem gerar hash, todas as senhas que são armazenadas podem ser roubadas se o armazenamento de dados for comprometido, e então imediatamente usadas para comprometer não só a aplicação ou serviço, mas também as contas dos usuários em outros serviços, se eles não usarem senhas únicas.

Pela aplicação de um algoritmo de hash para as senhas dos usuários antes de armazená-las, torna-se implausível para um atacante determinar a senha original, enquanto ainda é possível comparar o hash resultante com a senha original no futuro.

É importante observar, entretanto, que hash de senhas só as protegem de serem comprometidas no armazenamento de dados, mas não necessariamente as protegem de serem interceptadas por código malicioso injetado na aplicação ou serviço em si.

Por as funções de hash comuns tais como md5() e sha1() inadequados para senhas?

Algoritmos de hash como MD5, SHA1 e SHA256 são projetados para serem muito rápidos e eficientes. Com técnicas e equipamento de computação modernos, tornou-se trivial usar força bruta na saída desses algoritmos para de determinar a entrada original.

Por causa da rapidez com que um computador moderno pode reverter esses algoritmos de hash, muitos profissionais de segurança de informação sugerem fortemente que esses algoritmos não sejam utilizados para hash da senhas.

Como as senhas devem ter seus hashes gerados, se as funções de hash comuns não são adequadas?

Ao gerar hash de senhas, as duas considerações mais importantes são o custo do cálculo e o salt. Quando mais custoso computacionalmente for o algoritmo de hash, maior será o tempo para usar força bruta na sua saída.

O PHP fornece uma API de hash de senha nativo que lida de forma segura tanto a geração de hash quanto a verificação de senhas de forma segura.

O algoritmo sugerido para uso na geração de hash de senhas é o Blowfish, que também é o padrão usado pela API de hash da senha, pois é significativamente mais custoso computacionalmente que o MD5 ou que o SHA1, e ainda assim é escalável.

A função crypt() tabém está disponível para hash de senhas, mas ela só é recomendada para interoperabilidade com outros sistemas. No lugar dela, é fortemente encorajado o uso da API de hash de senha nativa sempre que possível.

O que é um salt?

Um salt de criptografia são dados que são aplicado durante o processo de hash para eliminar a possibilidade da saída ser pesquisada em uma lista de pares de hashes pré-calculados e suas entradas, conhecida como tabela arco-íris.

Em termos mais simples, um salt é uma pequena adição de dados, que faz com que os hashes sejam significativamente mais difíceis de serem decifrados. Há uma série de serviços na internet que fornecem listas extensas de hashes pré-computados, bem como a entrada original para esses hashes. O uso de um salt faz com que seja improvável ou impossível encontrar o hash resultante em uma destas listas.

password_hash() criará um salt aleatório se um não for fornecido, e esta geralmente é a abordagem mais fácil e mais segura.

Como os salts são armazenados?

Ao usar password_hash() ou crypt(), o valor de retorno inclui o salt como parte do hash gerado. Este valor deve ser armazenado na íntegra no banco de dados, uma vez que inclui informações sobre a função de hash que foi usada e pode, então, ser passado diretamente às funções password_verify() ao verificar senhas.

Aviso

password_verify() deve sempre ser usada ao invés se gerar novamente o hash e comparar o resultado a um hash armazenado para evitar ataques de temporização.

O diagrama a seguir mostra o formato de um valor de retorno de crypt() ou password_hash(). Como pode ser visto, eles são auto-contidos, com todas as informações sobre o algoritmo e o salt necessárias para verificações futuras de senhas.


        Os componentes do valor retornado pelas funções password_hash e crypt: em
        ordem, o algoritmo escolhido, as opções do algoritmo, o salt utilizado,
        e o hash da senha.

adicione uma nota

Notas Enviadas por Usuários (em inglês) 3 notes

up
147
alf dot henrik at ascdevel dot com
10 years ago
I feel like I should comment some of the clams being posted as replies here.

For starters, speed IS an issue with MD5 in particular and also SHA1. I've written my own MD5 bruteforce application just for the fun of it, and using only my CPU I can easily check a hash against about 200mill. hash per second. The main reason for this speed is that you for most attempts can bypass 19 out of 64 steps in the algorithm. For longer input (> 16 characters) it won't apply, but I'm sure there's some ways around it.

If you search online you'll see people claiming to be able to check against billions of hashes per second using GPUs. I wouldn't be surprised if it's possible to reach 100 billion per second on a single computer alone these days, and it's only going to get worse. It would require a watt monster with 4 dual high-end GPUs or something, but still possible.

Here's why 100 billion per second is an issue:
Assume most passwords contain a selection of 96 characters. A password with 8 characters would then have 96^8 = 7,21389578984e+15 combinations.
With 100 billion per second it would then take 7,21389578984e+15 / 3600 = ~20 hours to figure out what it actually says. Keep in mind that you'll need to add the numbers for 1-7 characters as well. 20 hours is not a lot if you want to target a single user.

So on essence:
There's a reason why newer hash algorithms are specifically designed not to be easily implemented on GPUs.

Oh, and I can see there's someone mentioning MD5 and rainbow tables. If you read the numbers here, I hope you realize how incredibly stupid and useless rainbow tables have become in terms of MD5. Unless the input to MD5 is really huge, you're just not going to be able to compete with GPUs here. By the time a storage media is able to produce far beyond 3TB/s, the CPUs and GPUs will have reached much higher speeds.

As for SHA1, my belief is that it's about a third slower than MD5. I can't verify this myself, but it seems to be the case judging the numbers presented for MD5 and SHA1. The issue with speeds is basically very much the same here as well.

The moral here:
Please do as told. Don't every use MD5 and SHA1 for hasing passwords ever again. We all know passwords aren't going to be that long for most people, and that's a major disadvantage. Adding long salts will help for sure, but unless you want to add some hundred bytes of salt, there's going to be fast bruteforce applications out there ready to reverse engineer your passwords or your users' passwords.
up
24
swardx at gmail dot com
8 years ago
A great read..

https://nakedsecurity.sophos.com/2013/11/20/serious-security-how-to-store-your-users-passwords-safely/

Serious Security: How to store your users’ passwords safely

In summary, here is our minimum recommendation for safe storage of your users’ passwords:

Use a strong random number generator to create a salt of 16 bytes or longer.
Feed the salt and the password into the PBKDF2 algorithm.
Use HMAC-SHA-256 as the core hash inside PBKDF2.
Perform 20,000 iterations or more. (June 2016.)
Take 32 bytes (256 bits) of output from PBKDF2 as the final password hash.
Store the iteration count, the salt and the final hash in your password database.
Increase your iteration count regularly to keep up with faster cracking tools.

Whatever you do, don’t try to knit your own password storage algorithm.
up
-2
tamas at microwizard dot com
3 years ago
While I am reading the comments some old math lessons came into my mind and started thinking. Using constants in a mathematical algorythms do not change the complexity of the algorythm itself.

The reason of salting is to avoid using rainbow tables (sorry guys this is the only reason) because it speeds up (shortcuts) the "actual" processing power.
(((Longer stored hashes AND longer password increases complexity of cracking NOT adding salt ALONE.)))

PHP salting functions returns all the needed information for checking passwords, therfore this information should be treated as constant from farther point of view. It is also a target for rainbow tables (sure: for much-much larger ones).

What is the solution?
The solution is to store password hash and salt on different places.
The implementation is yours. Every two different places will be good enough.

Yes, it will make problems for hackers. He/she needs to understand your system. No speed up for password cracking will work for him/her without reimplementing your whole system.

This is my two cent.
To Top