Sto cercando di sviluppare una piccola soluzione con la possibilità di autenticazione dell'utente. Poiché questo sarà in seguito consegnato ad altri sviluppatori, voglio che la prima iterazione della soluzione sia la più sicura possibile per i dati degli utenti.
Dopo aver letto alcune cose sulla sicurezza e l'hashing, ho deciso di memorizzare hash + salt in un singolo campo nel database. Il più delle volte salt negli esempi è lungo 16 byte. Ma tecnici avrebbero potuto avere qualsiasi dimensione.
Questo porta alla mia domanda:
- Se non è pubblico per quanto tempo il mio sale e il mio hash sono e li ho impostati su un valore "casuale", si tratta di un ulteriore passo per proteggere i dati degli utenti?
Se in qualche modo il database finisce in mani sbagliate, l'attaccante non saprebbe a che punto finisce il sale e inizia l'hash.
Dato un esempio in .net Core 2.0
che mi permette di impostare la lunghezza di sale e hash:
static Dictionary<string,string> _memDict = new Dictionary<string,string>() {{"username","password"}};
static int _amountOfHashBytes = 32;
static int _amountOfSaltBytes = 16;
static int _amountOfIterations = 10000;
static void Main(string[] args)
{
string passphrase = "passphrase";
string usrName = "dotnetcoreuser";
// 1) Create the salt value with a cryptographic
byte[] salt;
RandomNumberGenerator.Create().GetBytes(salt = new byte[_amountOfSaltBytes]);
Console.WriteLine("Salt: " + Convert.ToBase64String(salt));
// 2) Create the Rfc2898DeriveBytes and get the hash value
var pbkdf2 = new Rfc2898DeriveBytes(passphrase, salt, _amountOfIterations);
byte[] hash = pbkdf2.GetBytes(_amountOfHashBytes);
Console.WriteLine("Hash: " + Convert.ToBase64String(hash));
// 3) Combine the salt and password bytes for later use
byte[] hashBytes = new byte[_amountOfSaltBytes + _amountOfHashBytes];
Array.Copy(salt, 0, hashBytes, 0, _amountOfSaltBytes);
Array.Copy(hash, 0, hashBytes, _amountOfSaltBytes, _amountOfHashBytes);
// 4) Turn the combined salt + hash into a string for storage
string savedPasswordHash = Convert.ToBase64String(hashBytes);
Console.WriteLine("Combine: " + savedPasswordHash);
_memDict.Add(usrName, savedPasswordHash);
// 5) Verify the user-entered password against a stored password.
string tryInput = "1234";
Console.WriteLine("Try: {0} Result: {1}", tryInput, IsValidPassword(tryInput, usrName));
tryInput = "password";
Console.WriteLine("Try: {0} Result: {1}", tryInput, IsValidPassword(tryInput, usrName));
tryInput = "passphrase";
Console.WriteLine("Try: {0} Result: {1}", tryInput, IsValidPassword(tryInput, usrName));
}
static bool IsValidPassword(string userInput, string userName)
{
string savedPasswordHash = _memDict[userName];
byte[] hashBytes = Convert.FromBase64String(savedPasswordHash);
byte[] salt = new byte[_amountOfSaltBytes];
Array.Copy(hashBytes, 0, salt, 0, _amountOfSaltBytes);
var pbkdf2 = new Rfc2898DeriveBytes(userInput, salt, _amountOfIterations);
byte[] hash = pbkdf2.GetBytes(_amountOfHashBytes);
for (int i=0; i < _amountOfHashBytes; i++)
{
if (hashBytes[i+_amountOfSaltBytes] != hash[i]) { return false; }
}
return true;
}