I have spent about 2 weeks reading about hashing passwords and website security. As there seems to be many different ways to achieve this, I'm a bit confused as to whether my code is secure.
Can anyone please have a look through and let me know what they think and what needs to be changed?
I have not added any error trapping yet, as to allow me to concentrate on just the hashing.
The steps I have took are as follows:
- Create random salt
- Add random salt and email together to create salt for password (email will be encrypted in database)
- Hash password and salt and store in 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);
}
RNGCryptoServiceProvider
,SHA256Managed
andRfc2898DeriveBytes
all implement theIDisposable
interface and therefore should be wrapped inusing
statements. \$\endgroup\$saltBytes
array. Won't this be different every time your method is called? It just seems like one instance will never be able to access a previous instance, so how will you ever be able to check a password later? I'm probably missing something. \$\endgroup\$