Izuagbala
Izuagbala

Reputation: 449

How to salt and compare password in ASP.NET MVC

I am writing an ASP.NET program where I need to store the users password in the database. But I get a password mismatched when I Compare the password from the database with the user input password. Even if the users password is correct.

Password Hashing:

string PasswordSalt = Crypto.HashPassword(DateTime.Now.ToString());
string hashPassword = Crypto.HashPassword(formcollection["PassWord"]); //Hash User PassWord
user.PassWord = Crypto.HashPassword(PasswordSalt + hashPassword);//Add Salt to Password For Futher Security
user.PassWordSalt = PasswordSalt;

Password Verification:

Users ThisUser = Users.UsersGetByEmail((string)Session["email"]);
string checkpassword = ThisUser.PassWord;

//User Inputed password.
string password = user.PassWord;

if (password != null)
{
    //Need to fix.
    string encrypt_password = Crypto.HashPassword(password);
    string salted_password = Crypto.HashPassword(ThisUser.PassWordSalt + encrypt_password);
    //bool does_password_match = Crypto.VerifyHashedPassword(checkpassword, password);
    if (checkpassword == salted_password)
    {
        //Check if the inputed password matches the password from the Database.

        //Remember to give session based on the user_id.
        Session["user_id"] = ThisUser.Id;
        return RedirectToAction("Promise");


    }
    else
    {

        ModelState.AddModelError("PassWord", "Wrong Password, Please Enter Correct Password");
        return View(user);
    }

Upvotes: 1

Views: 3411

Answers (1)

Kevin Nelson
Kevin Nelson

Reputation: 7663

I've never used it, but based on the documentation...

Crypto.HashPassword adds the salt for you and returns a base-64 encoded string with all the details in it to verify the password. So, you do NOT need to add a salt yourself.

All you need to do is store the hash result (base64EncodedHash below) in the DB, and then use it with VerifyHashedPassword to authenticate later. E.g. make a unit test like so:

var base64EncodedHash = Crypto.HashPassword("password");
Assert.IsTrue( Crypto.VerifyHashedPassword( base64EncodedHash, "password" ) );
Assert.IsFalse( Crypto.VerifyHashedPassword( base64EncodedHash, "otherPass") );

https://msdn.microsoft.com/en-us/library/system.web.helpers.crypto.verifyhashedpassword(v=vs.111).aspx

To translate this to your code:

user.PassWord = Crypto.HashPassword(formcollection["PassWord"]);

Then to verify (comments added for quirks I see):

//Why are you storing "email" in Session before user is validated??? Seems off.
Users ThisUser = Users.UsersGetByEmail((string)Session["email"]);
string userInputPassword = user.PassWord; //this should be coming from POST

if( ThisUser != null && Crypto.VerifyHashedPassword(ThisUser.PassWord, userInputPassword) ) {
    Session["user_id"] = ThisUser.Id;
    return RedirectToAction("Promise");
}
else {
    ModelState.AddModelError("PassWord","Your username or password are incorrect");
    return View(user);
}

Ideally, as I somewhat indicated by my change of your error text...you also want to give the user the same error message whether the username/email or password are wrong. Your code, as is, probably returns a different error if the email doesn't return an account, but you don't want to give that much info to brute-force attackers.

You also need to put in some brute-force checking so that if they attempt too many times with failures, block that IP address for X amount of time., etc.

And, as someone said...when it comes to security...until you're the expert...it's best to use pre-existing code/frameworks to mitigate you risks.

Upvotes: 2

Related Questions