Gordon Saxby
asked on
Secure passwords in a ASP.NET 2.0 Custom Membership Provider
I am writing a custom membership provider which gets logon info from a 3rd party database.
I have it working using plain text passwords but I want to secure the passwords - ideally, so that I can retain the "forgot password" feature but if I have to change it to "reset password" functionality then I could.
Basically, I just need a clear explanation of how I go about encrypting or hashing the password when it is first created and how to validate a user logon. Also, how to retrieve or reset the password, depending on which method is used (encrypt or hash?)
I have it working using plain text passwords but I want to secure the passwords - ideally, so that I can retain the "forgot password" feature but if I have to change it to "reset password" functionality then I could.
Basically, I just need a clear explanation of how I go about encrypting or hashing the password when it is first created and how to validate a user logon. Also, how to retrieve or reset the password, depending on which method is used (encrypt or hash?)
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Well with hash the user won't be able to retrieve the current password because hash is one-way encryption, if you want to give them the retrieve current password possibility, you could use this utility to encrypt and decrypt your passwords with a symmetric method.
public class Symetric
{
public static string Encrypt(string plainText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = password.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
memoryStream.Dispose();
cryptoStream.Close();
cryptoStream.Dispose();
string cipherText = Convert.ToBase64String(cipherTextBytes);
return cipherText;
}
public static string Decrypt(string cipherText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = password.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
memoryStream.Dispose();
cryptoStream.Close();
cryptoStream.Dispose();
string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
return plainText;
}
}
You will want to invoke some cryptographic measures as others have described. Specifically you should be looking at a cryptographic hash algorithm such as SHA-256. You can find an article about both cryptographic hash functions and specifically SHA-256 on Wikipedia.
One thing you will want to make sure to prevent against is dictionary attacks, you can do this by creating a unique salt value for each password. Make sure that the salts are generated using a cryptographically secure random number generator.
http://en.wikipedia.org/wiki/Cryptographic_hash_function
http://en.wikipedia.org/wiki/Sha256#SHA-2_family
http://en.wikipedia.org/wiki/Cryptographic_salt
http://en.wikipedia.org/wiki/CSPRNG
If you are paranoid as well, you can resist against brute force attacks and length extension attacks by using HMAC on top of SHA-256.
http://en.wikipedia.org/wiki/HMAC
Enjoy.
One thing you will want to make sure to prevent against is dictionary attacks, you can do this by creating a unique salt value for each password. Make sure that the salts are generated using a cryptographically secure random number generator.
http://en.wikipedia.org/wiki/Cryptographic_hash_function
http://en.wikipedia.org/wiki/Sha256#SHA-2_family
http://en.wikipedia.org/wiki/Cryptographic_salt
http://en.wikipedia.org/wiki/CSPRNG
If you are paranoid as well, you can resist against brute force attacks and length extension attacks by using HMAC on top of SHA-256.
http://en.wikipedia.org/wiki/HMAC
Enjoy.
ASKER
After reading more about this, it seems that the best solution is the one-way hash. If the user forgets their password, issue them a new one.
Mahone7 - your solution looks very simple to implement, but I can't quite figure out (yet ... still looking!) whether there is "salting" involved!
urir10 - the example on the page you linked to looks good as well, although it is built to handle multiple options which I wouldn't need. It seems as though you do not need to store the salt value as the routine "VerifyHash" works out what the salt value was ... am I reading that right?
Mahone7 - your solution looks very simple to implement, but I can't quite figure out (yet ... still looking!) whether there is "salting" involved!
urir10 - the example on the page you linked to looks good as well, although it is built to handle multiple options which I wouldn't need. It seems as though you do not need to store the salt value as the routine "VerifyHash" works out what the salt value was ... am I reading that right?
the first solution dose not use salt in the implementation " this is simple one way hashing" and its enough for small to mid size web application you don't need to Add Salt to your plain text password but any way check this for more info : http://www.obviex.com/samples/hash.aspx you may need use salt
this is modified version that use salt
public static String HashPassword(String Pass,String Salt)
{
System.Security.Cryptograp hy.SHA256C ng sha = new System.Security.Cryptograp hy.SHA256C ng();
UnicodeEncoding uniEnc = new UnicodeEncoding();
Byte[] hashedPassword = sha.ComputeHash(uniEnc.Get Bytes(Pass ) + uniEnc.GetBytes(Salt));
String password = "";
foreach (Byte item in hashedPassword)
{
password += String.Format("{0:x2}", item);
}
//End of Computation
return password;
}
this is modified version that use salt
public static String HashPassword(String Pass,String Salt)
{
System.Security.Cryptograp
UnicodeEncoding uniEnc = new UnicodeEncoding();
Byte[] hashedPassword = sha.ComputeHash(uniEnc.Get
String password = "";
foreach (Byte item in hashedPassword)
{
password += String.Format("{0:x2}", item);
}
//End of Computation
return password;
}
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Im not exactly sure 100% what VerifyHash does, but there is no way to decrypt the hashed value. The salt is only an additional security, you can go fine without it.
ASKER
urir10 first gave the link to a useable solution. Mahone7 provided another solution which I used in combination to produce my required solution.
public static String HashPassword(String Pass)
{
System.Security.Cryptograp
UnicodeEncoding uniEnc = new UnicodeEncoding();
Byte[] hashedPassword = sha.ComputeHash(uniEnc.Get
String password = "";
foreach (Byte item in hashedPassword)
{
password += String.Format("{0:x2}", item);
}
//End of Computation
return password;
}
then after hashing the password store it in the database insted of the clear password
and each time your user login you should hash the entered password by the user and compare it by the hashed password that you stored it in the database.
check this http://www.codeproject.com/KB/recipes/StoringPasswords.aspx for more info