Salted Password Hashing: Login Più Sicure

Grazie al meccanismo delle “Salted Password Hashing” è possibile generare password sicure per il proprio sistema di Login. Ora, senza entrare nel vivo della teoria, provo a spiegarvi il perché utilizzarlo.

  • Caso Tradizionale: Più utenti con la stessa password hanno lo stesso hash 

  • Case Salted Password Hashing: Ogni utente avrà il suo hash anche se la password è la stessa

ATTENZIONE: Questo articolo è da prendere come “AS IS” senza garanzie per l’utilizzo e/o per problemi di validazione delle password generate con esso. Si consiglia vivamente una fase di test come nell’esempio riportato in fondo all’articolo.

Salted Password Hashing: Esempio a “parole”

Per questo tutorial utilizzeremo una classe dal nome SaltedPasswordHashing. Al suo interno troveremo:

  • Classe UserLogin: contiene informazioni minimali dell’utente (ID, Name, Password)

  • CreateNewUser: passando in ingresso Name e Password otterremo una istanza di “UserLogin” con la password criptata

  • GeneratePasswordHash: Il metodo vero e proprio che consente di ottenere la “password personalizzata” per ogni utente. In questo metodo utilizzeremo anche una costante  dal nome SITE_PRIVATE_KEY per inserire un terzo elemento  per rendere più ardua l’identificazione. Cambiare SITE_PRIVATE_KEY implica il non accedere/validare più le password precedenti.

Salted Password Hashing: The Source Code

UserLogin

1
2
3
4
5
6
7

 public class UserLogin {
            public Guid UserID { get; set; }
            public string UserName { get; set; }
            public string PasswordHash { get; set; }
        }

CreateNewUser

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

        public UserLogin CreateNewUser(string userName, string password)
        {
            UserLogin result = new UserLogin()
            {
                UserID = Guid.NewGuid(),
                UserName = userName
            };
            result.PasswordHash = GeneratePasswordHash(result.UserID, password);
            return result;
        }

GeneratePasswordHash

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

private string GeneratePasswordHash(Guid userId, string password)
        {

            string result = string.Empty;

            using (HashAlgorithm hashAlgorithm = MD5.Create())
            {
                byte[] byteToComputeHash = Encoding.ASCII.GetBytes($"{userId}{SITE_PRIVATE_KEY}{password}");
                byte[] hashValue = hashAlgorithm.ComputeHash(byteToComputeHash);

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashValue.Length; i++)
                {
                    sb.Append(hashValue[i].ToString("X2"));
                }
                result = sb.ToString();
            }


            return result;
        }

Nel codice proposto ho usato la criptografia MD5. E’ possibile utilizzare altri sistemi, a patto che derivino da HashAlgorithm. Un esempio?

  • System.Security.Cryptography.KeyedHashAlgorithm
  • System.Security.Cryptography.MD5
  • System.Security.Cryptography.RIPEMD160
  • System.Security.Cryptography.SHA1
  • System.Security.Cryptography.SHA256
  • System.Security.Cryptography.SHA384
  • System.Security.Cryptography.SHA512

Come vedete le possibilità non mancano.

Salted Password Hashing: Generazione &  test

Il codice appena proposto è molto semplice, ora vediamo in maniera altrettanto semplice come scatenare il tutto e verificare se la password generata è sempre la stessa oppure no. TestPasswordHash

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

     public void TestPasswordHash()
            {

                List<string> users = new List<string>(){
                    "864A426C-8203-4F6C-A623-C81C6F51C528",
                    "35772B55-5564-4B53-BF42-F3B51B60E087",
                    "818EFD94-5748-4A6F-9887-E95787D4B3D0",
                    "A11EC61C-CAF2-4F09-BAC0-4FF759AFCCC1",
                    "05FA6DAC-B221-4B4B-9DB1-D40D8FDCE327",
                    "78032810-B373-42F0-91B1-BD5FB548ED8E",
                    "4D5E7464-5AD3-4CAD-96CF-576749D5729C",
                    "5F017854-37D4-4266-9DEA-A78D33DEC23E",
                    "BFFD4348-B377-4FE6-AFBA-55667391BBCF",
                    "6AD031FB-15DD-4977-B384-4E2F40C60368",
                };

                SortedDictionary<Guid, string> userWithPassword = new SortedDictionary<Guid, string>();

                string userPassword = "password";
                foreach (var item in users)
                {
                    userWithPassword.Add(
                        new Guid(item),
                        this.GeneratePasswordHash(new Guid(item), userPassword)
                    );
                }

                foreach (var item in userWithPassword)
                {
                    string password = this.GeneratePasswordHash(item.Key, userPassword);
                    Console.WriteLine($"{item.Key}|{item.Value}|{password}|{item.Value== password}");
                }

            }

In cosa consiste l’esempio proposto? Come prima cosa parto da un elenco di Guid generate al volo e cablate per averle identiche ad ogni lancio del test. In seguito si lanciano due lanci del generatore di password. Il primo per memorizzarla ed il secondo per confrontarla con quella appena generata. Se le due password coincidono è corretto.

Adding Salt to Hashing: A Better Way to Store Passwords