Password encryption

Asked

Viewed 2,359 times

5

I have an Asp.Net MVC project and would like to securely store users' passwords in the bank.

The goal is to create something that cannot be easily decrypted in a few hours of brute force on a PC (I am aware that with a little more effort the password can be broken). So a simple hash nay would be ideal, like: MD5, SHA1, SHA2, etc. I believe that some symmetric algorithm like AES would solve the problem.

What I’ve got planned:
When creating the account, the user’s password is encrypted and saved in the database. inserir a descrição da imagem aqui
When logging in, the bank password will not be decrypted, but the password entered by the user in the login will be encrypted and compared with the bank password. inserir a descrição da imagem aqui Disregard whether the code present in the image is not correct.

Doubts are:

  1. That would be good practice?
  2. AES would be the best algorithm?
    2.1. How could AES encryption be done using C#? Is there already a ready method?
    2.2. Are there any more secure key models?
  • 1

    http://answall.com/q/2402/101

  • I gave two answers here. I don’t know if this is a case of replying, but if you need me, just say the word.

  • Look at the question the mustache quoted. Using AES to protect passwords is tricky because if an attacker gains access to your server he also gains access to the symmetric encryption key... An asymmetrical encryption improves things a bit (since you will never "reverse", you can keep the private key off the server), but still has drawbacks compared to a slow hash.

2 answers

11


That would be good practice?

No. The recommended way to protect a password is through a slow hash (PBKDF2, Bcrypt or scrypt). You’re right to say that a fast hash is ineffective, but you realize that using an encryption algorithm in a single direction (just cipher, never decrypt) is almost the same as applying a hash?

It would not be honest of me to say that your technique is at all useless, however - if an attacker gets access to the database and only to the database (for example, exploiting an SQL Injection) it will be unable to recover passwords. In fact, there is a technique called "Pepper" (pepper) that takes advantage of this fact to give additional protection to a hash (more details in the answer to the linked question). The problem lies in basing all your security only on this premise (that the attacker will not have access to your code, even if only for reading).

AES would be the best algorithm?

Neither AES nor any other symmetric encryption algorithm (such as 3DES, etc). For even if you Do not intend to decipher the password at any time, an attacker who gets access to your BD and your key will not do the same "as a matter of honor", it will undo the encryption and that’s it! That is, your login is slow (because you keep encrypting and comparing) and the attacker continues fast...

An asymmetric cipher would be marginally better (so the attacker could not simply decipher, he would have to password test - because if the private key is off the server, he can only use the public key) but she still runs the risk of being overly quick about a hash. Ultimately, a hash is even the best you can reasonably achieve, and the use of a Pepper would help give the additional security you sought when proposing this method (although I personally don’t usually worry too much about it - it will depend on how sensitive your particular application is).

How could it be done the AES encryption a slow hash using C#? There is already some ready method?

The Rfc2898DeriveBytes is a native solution for PBKDF2 (there are also implementations Bcrypt and scrypt for .Net, third party I believe), you just need to adjust the parameters accordingly. An example would be:

string pwd = senha_a_ser_hasheada;

// Cria um sal aleatório de 64 bits
byte[] salt = new byte[8];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
    // Enche o array com um valor aleatório
    rngCsp.GetBytes(salt);
}

// Escolha o valor mais alto que seja "tolerável"
// 100 000 era um valor razoável em 2011, não sei se é suficiente hoje
int myIterations = 100000;
try
{
    Rfc2898DeriveBytes k = new Rfc2898DeriveBytes(pwd, salt, myIterations);
    byte[] hash = k.getBytes(32);
    // Codifica esse hash de alguma forma e salva no BD
    // (lembre-se de salvar o salt também! você precisará dele para comparação)

If you want to use one Pepper, take the secret value that only exists in your code (or in some configuration file) and attach it to the password or salt before hashing (adjusting the size if necessary).

  • I saw good feedback, but your response was spectacular. Is there a bit limit to salt? What exactly would the interactions be? I looked on that website I got some information, but there were still these two doubts.

  • 1

    I don’t know if there are limits, 8 bytes is the minimum, but it can be more if you want (I don’t see much point in using more bits in salt than in the generated hash, however). It’s more important that the salt be single per user than secret, so you can store it in the bank next to the hash without any protection. Iterations are what make the hash slow - PBKDF2 basically calls a hash function (in this case, SHA-1, and technically speaking is an HMAC and not a hash) over and over again, and you are the one who specifies the number of repetitions. This sets a minimum time required for the attacker to try each

  • 1

    password candidate. The documentation says 1000 is the recommended minimum, but it was the recommended minimum years ago, today 10,000 or more is better (and "serious" libraries use 100,000 or more). See how long it takes to generate a hash using 100,000 on your server, if you think it’s too slow, decrease it, or increase it. The ideal is that the number is as high as possible, since of course the user does not stay 10s waiting for a login hehe. Ah, and if you plan to change the number of iterations in the future, save it in the bank as well, otherwise old users won’t be able to log in.

  • A flexible medium is using a "password" field that contains <algorithm>$<iterations>$<salt>$<hash>. Ex.: PBKDF2$100000$kXNppmR0lbE=$oxRYXBtozKLv6KnAGQ==. So in the future your system can evolve without compromising old users.

  • in this case a possible attacker would not be holding the knife and cheese in hand?

  • 2

    @Jedaiasrodrigues Why is that? The premise of a well-made hash is that given an algorithm, a working factor, a salt and a hash, it is not possible in a timely manner to find a password that produces that same hash (i.e. reverse the hash). All this data is necessary because to check whether a password is correct or not you will have to redo this process.

  • 2

    You’re right! My premise that the important thing is not to give information and create an "unbreakable" password is wrong. While reading the results of my research, I realized that the important thing is that the time to break this hash makes it impossible to try it.

  • Would you have the link with that quote? (i.e. reverse the hash)

  • by using the '$' pattern to separate the parameters in my concatenation there would be no risk of my hash being generated with '$' generating problems? Or the hash will NEVER possess '$'?

  • @Jedaiasrodrigues I don’t have a specific quote, but the Wikipedia article in English explains reasonably well this property ("pre-image resistance" and "it is impracticable to generate a message from its hash") - which is the raison d'être of a cryptographic hash function, by the way. As to the $, I was inspired by in the way Django does. If both salt and hash are encoded in Base64 (or Hex, or decimal) then there is no way to display a $.

Show 5 more comments

1

Use bcrypt: https://www.nuget.org/packages/BCrypt-Official.
Read the definition of what bcrypt is: https://pt.wikipedia.org/wiki/Bcrypt.
With bcrypt your passwords are already encrypted with salt/hash.
When checking the user when logging in, there is the function Verifypassword that receives 2 parameters which are: password coming from the textbox and password coming from the database; the hashes are compared.
To use, include these lines according to the chosen language:

using Bcrypt.Net.Bcrypt -> C# 
Imports Bcrypt.Net.Bcrypt -> VB.NET


To hash the password:

HashPassword(senha, GenerateSalt(12)).

12 is the standard cost. The higher the cost, the slower it will be for Rainbow Tables attacks, but also for hash generation processing by your hardware and database.

For verification:

VerifyPassword(campo_senha, banco_senha)

Example in VB.NET (can be easily ported to C#):

If sqldatareader.Hasrows Then
   While sqldatareader.Read
   If (Bcrypt.Net.Bcrypt.Verify(txtPassword.text, 
       sqldatareader.Item("password"))) then
       MsgBox("Valid Credentials")
     else
       MsgBox("Invalid Credentials")
   End if
   End While
   sqldatareader.Close()
End If

sqldatareader: datareader that can be mysql, sql, postgresql, etc.
To store the password in the database use varchar(255) or nvarchar(255) in the case of sql server.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.