Password di hashing prima di inserirli nel database

4

Nelle ultime 2 settimane ho letto molti blog sulla sicurezza dei siti Web e sulle password di hashing.

Molti siti hanno menzionato i pro ei contro su diversi modi per farlo, il che mi ha lasciato un po 'confuso su quanto sia sicuro il mio codice.

Se possibile, qualcuno può dare un'occhiata al codice qui sotto e fammi sapere cosa ne pensano. Ho postato questa domanda su link ma ancora spento, nessuno ha risposto.

I passaggi che ho seguito sono i seguenti:

  1. Crea salt random
  2. Aggiungi salt salt e email casuali per creare sale per la password (l'email verrà crittografata nel database)
  3. Inserisci la password e sali e archivia nel database

    public const int HashBytes = 128;
    public const int DefaultIterations = 10000;
    
        //Create random bytes for salt
        public static string SaltSHA256()
        {
            const int minSaltSize = 8;
            const int maxSaltSize = 16;
            var random = new Random();
            int saltSize = random.Next(minSaltSize, maxSaltSize);
            byte[] saltBytes = new byte[saltSize];
            var rng = new RNGCryptoServiceProvider();
            rng.GetNonZeroBytes(saltBytes);
            HashAlgorithm hash = new SHA256Managed();
            byte[] bytes = hash.ComputeHash(saltBytes);
            return Convert.ToBase64String(bytes);
        }
    
        //Create salt using email and public static string SaltSHA256()
        //Store email and public static string SaltSHA256() in database
        public static string SaltRfc2898(string email,string hashedSalt)
        {
            var salt = new Rfc2898DeriveBytes(email, Encoding.UTF8.GetBytes(hashedSalt), DefaultIterations);
            return Convert.ToBase64String(salt.GetBytes(HashBytes));
        }
    
        //Hash password and salt
        public static string PasswordHashRfc2898(string password,string salt)
        {
            var hashedPassword = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), DefaultIterations);
            return Convert.ToBase64String(hashedPassword.GetBytes(HashBytes));
        }
        //Get salt and password from database based on username
        //Salt in from data created by public static string SaltSHA256()
    
        public static bool DoPasswordsMatch(string password,string salt)
        {
            //Password would be pulled from db based on username
            byte[] testPassword = Convert.FromBase64String("basestring");
    
            //Salt would be pulled from database based on username
            var saltFromDatabase = salt;
    
            //Hash password and salt
            var hashUserInputAndSalt = PasswordHashRfc2898(password, saltFromDatabase);
    
            //Convert to byte[] ready for comparison
            byte[] convertUserInputFromBase64 = Convert.FromBase64String(hashUserInputAndSalt);
    
            //Compare and return true or false
            return convertUserInputFromBase64.SequenceEqual(testPassword);
    
        }
    
posta George Phillipson 05.09.2013 - 15:01
fonte

1 risposta

9

Stai utilizzando Rfc2898DeriveBytes , che implementa PBKDF2 : va bene.

Stai producendo un urlo di 128 byte di output, che è un overkill totale per la verifica della password. 128 bit , cioè 16 byte, sono sufficienti. 128 byte non danno alcun danno, tranne che fanno in modo che le stringhe più ingombranti vengano memorizzate nel tuo database.

Stai usando 10000 iterazioni, che potrebbero essere un po 'basse. Più alto è meglio. 10000 non è cattivo , ma è probabile che il tuo server ne possa gestire dieci volte di più e questo renderebbe gli attacchi dieci volte più difficili. Ti suggerisco di provare diversi valori e di rendere il conteggio il più alto tollerabile sul tuo sistema, relativamente al carico di lavoro previsto (ovvero il numero di utenti che si connettono al secondo).

I problemi di codifica sono meglio mantenuti separati, a livello di archiviazione. Il sale, l'output PBKDF2 e la password sono tutte sequenze di byte. Rfc2898DeriveBytes può codificare una String password per te, ma si aspetta che il sale sia "alcuni byte" e produce un output che è "alcuni byte". Per l'archiviazione nel database, utilizzare le capacità di quel database per elaborare dati binari o applicare alcune codifiche, come Base64 . Quindi, usa Convert.FromBase64String() e Convert.ToBase64String() prima di scrivere e dopo aver letto, rispettivamente. La combinazione di Base64 e UTF-8 è confusa e rende la lettura (auditing) del codice un compito più difficile.

La tua generazione di sale è strana:

  • Non ha senso impostare la variabile della lunghezza del sale. Basta andare per 16 byte.
  • Non ha senso evitare i byte di valore 0. A salt è una sequenza di byte , non di caratteri e 0 non è speciale.
  • Non c'è motivo di tagliare il sale.

Un sale corretto per PBKDF2 è una sequenza di almeno 8 byte e univoco (il più possibile). 16 byte riempiti con l'output di un strong PRNG sono un buon modo per farlo. Anche un GUID funzionerebbe (i sali non devono essere casuali, devono solo essere riutilizzati il più raramente possibile dai normali sistemi, e GUID lo fanno). In .NET, se vuoi generare un buon salt, fallo semplicemente:

byte[] salt = Guid.NewGuid().ToByteArray();

In alternativa, Rfc2898DeriveBytes può generare il sale per te. Al momento della registrazione dell'utente o della modifica della password, procedere come segue:

Rfc2898DeriveBytes h = new Rfc2898DeriveBytes(password, saltSize, iterations);
byte[] hashedPassword = h.GetBytes(hashSize);
byte[] salt = h.Salt;
// then store salt and hashedPassword in the database

e, previa verifica:

byte[] salt = ... // read from database
Rfc2898DeriveBytes h = new Rfc2898DeriveBytes(password, salt, iterations);
byte[] hashedPassword = h.GetBytes(hashSize);
// compare hashedPassword with the stored value

puoi invocare un PRNG separato come RNGCryptoServiceProvider() e fare le cose da te; questo è quello che fai, e anche se è strano , non è debole . Tuttavia, è più semplice utilizzare ciò che il sistema già fornisce. Il codice più semplice di solito significa meno bug.

    
risposta data 05.09.2013 - 15:33
fonte

Leggi altre domande sui tag