asp.net "Remember Me" cookie - asp.net

I have implemented remember me option in my asp.net webform by using this,
protected void LBtnSubmit_Click(object sender, EventArgs e)
{
if (this.ChkRememberme != null && this.ChkRememberme.Checked == true)
{
HttpCookie cookie = new HttpCookie(TxtUserName.Text, TxtPassword.Text);
cookie.Expires.AddYears(1);
Response.Cookies.Add(cookie);
}
}
Am i doing it the right way? Any suggestion.. I am using windows authentication and i am not using asp.net membership..

Rather than directly storing the username and password in the cookie, store the username and a hash of the password and a salt in the cookie, then when you authenticate the cookie, retrieve the password for the given username, re-create the hash with the password and the same salt and compare them.
Creating the hash is as simple as storing the password and salt values together in a string, converting the string to a byte array, computing the hash of the byte array (using MD5 or whatever you prefer) and converting the resulting hash to a string (probably via base64 encoding).
Here's some example code:
// Create a hash of the given password and salt.
public string CreateHash(string password, string salt)
{
// Get a byte array containing the combined password + salt.
string authDetails = password + salt;
byte[] authBytes = System.Text.Encoding.ASCII.GetBytes(authDetails);
// Use MD5 to compute the hash of the byte array, and return the hash as
// a Base64-encoded string.
var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] hashedBytes = md5.ComputeHash(authBytes);
string hash = Convert.ToBase64String(hashedBytes);
return hash;
}
// Check to see if the given password and salt hash to the same value
// as the given hash.
public bool IsMatchingHash(string password, string salt, string hash)
{
// Recompute the hash from the given auth details, and compare it to
// the hash provided by the cookie.
return CreateHash(password, salt) == hash;
}
// Create an authentication cookie that stores the username and a hash of
// the password and salt.
public HttpCookie CreateAuthCookie(string username, string password, string salt)
{
// Create the cookie and set its value to the username and a hash of the
// password and salt. Use a pipe character as a delimiter so we can
// separate these two elements later.
HttpCookie cookie = new HttpCookie("YourSiteCookieNameHere");
cookie.Value = username + "|" + CreateHash(password, salt);
return cookie;
}
// Determine whether the given authentication cookie is valid by
// extracting the username, retrieving the saved password, recomputing its
// hash, and comparing the hashes to see if they match. If they match,
// then this authentication cookie is valid.
public bool IsValidAuthCookie(HttpCookie cookie, string salt)
{
// Split the cookie value by the pipe delimiter.
string[] values = cookie.Value.Split('|');
if (values.Length != 2) return false;
// Retrieve the username and hash from the split values.
string username = values[0];
string hash = values[1];
// You'll have to provide your GetPasswordForUser function.
string password = GetPasswordForUser(username);
// Check the password and salt against the hash.
return IsMatchingHash(password, salt, hash);
}

I would not store the users password in a cookie... Rather store the user id and the ip address in the cookie.

I would not store the ip / user id in the cookie. Session highjacking would then be really easy, I mean I know the username / ip of my collegues, I could add that cookie to my message and then I can work on the session of my collegue.

Related

Bcrypt Verify always returning false

[HttpPost("signUp")]
public async Task<ActionResult<Users>> PostUserRegister(Users user)
{
if (userEmailExists(user.Email))
{
return BadRequest();
}
string salt = BC.GenerateSalt(12);
// hash password
user.Password = BC.HashPassword(user.Password, salt);
_context.Database.ExecuteSqlRaw("EXECUTE dbo.UserRegister #userName, #firstName, #lastName, #Password, #userEmail, #gender, #dob",
new SqlParameter("#userName", user.UserName.ToString()),
new SqlParameter("#firstName", user.FirstName.ToString()),
new SqlParameter("#lastName", user.LastName.ToString()),
new SqlParameter("#Password", user.Password.ToString()),
new SqlParameter("#userEmail", user.Email.ToString()),
new SqlParameter("#gender", user.Gender.ToString()),
new SqlParameter("#dob", user.Dob));
/* var format = "dd/MM/yyyy";
var date = DateTime.ParseExact(user.Dob, format);*/
return Ok(user);
//_context.Users.Add(users);
//await _context.SaveChangesAsync();
//return CreatedAtAction("GetUsers", new { id = users.UserId }, users);
}
Im siging a new user up like this. Hashing the password using Bcrypt.
using BC = BCrypt.Net.BCrypt;
[HttpPost("login")]
public async Task<ActionResult<Users>> PostUserLogin(Users user)
{
// get account from database
var account = _context.Users.SingleOrDefault(x => x.Email == user.Email);
// check account found and verify password
if (account == null || !BC.Verify(user.Password, account.Password))
{
// authentication failed
return Unauthorized(user);
}
else
{
// authentication successful
return Ok(user);
}
Then I try to verify the password in the login function. When i debug to see the values of user.Password and account.Password they are correct. the user.Password is equal to the password the user entered to register and the account.Password is the Hashed password stored in the database. I was trying to follow this tutorial ASP.NET Core 3.1 - Hash and Verify Passwords with BCrypt
I have read the blog you provided. And I think we should double check below points.
The format of Password in your db,if the orginal password is 11, then the value stored should like :
$2a$12$NTuJLk9/xZnlxP.oFj1mu.1ZypqYP4YuS1QbTBy7ofJwzKLSEEVBq
In this line BC.Verify(user.Password, account.Password),
The value of user.Password
user.Password == 11
And the value of account.Password
account.Password == $2a$12$NTuJLk9/xZnlxP.oFj1mu.1ZypqYP4YuS1QbTBy7ofJwzKLSEEVBq
Please double check it, if you still have some issue, you can add the picture with debugging result.
i have same problem with bCrypt like you.
The main problem was much simpler than I thought. The main reason for this was that I used uppercase and lowercase letters when I received and saved the password.
I Fixed this problem with make my password input to lower and save it to db
And When i want to verify it , i make the password lowercase again .
user.Password = BC.HashPassword(user.Password.ToLower(), salt);
and when you want to Verify , use it like this:
if (account == null || !BC.Verify(user.Password.ToLower(),account.Password))
I Think This is your question Answer.

ResetPassword method in ASP.NET 4.0

The ResetPassword method is able to reset the old password, but user is unable to login with the new password generated by ResetPassword method. Code:
String user =(((TextBox)PasswordRecovery2.Controls[0].FindControl("UserName")).Text).ToString();
String newPassword = clsStatic.RandomString(10, false);
MembershipUser username = Membership.GetUser(user);
String resetPassword = username.ResetPassword();
username.ChangePassword(resetPassword, newPassword);
Does ChangePassword return true or false?
I bet your random function returns a password that doesn't meet the criteria specified in your web.config membership section.
Since ResetPassword already gives you a new valid password why do you have to generate another one?
Your new random password generation should be ideally one of these
// this will automatically take care of all password criteria
string newPassword = Membership.GeneratePassword(10, 0);
Or
//for generating alpha numeric password only
string newPwd = Guid.NewGuid().ToString().Substring(0, 11).Replace("-", "");
Your code is fine otherwise.

Migrating from SqlMembershipProvider to Custom Provider

Here's the scenario:
I have used default SqlMembershipProvider to implement membershp on a website. Now I'd like to migrate to my custom membership provider. (FYI the provide I use is CodeFirst Membership Provider.
The problem is, the provider uses a custom encryption/hash algorithm to store passwords in db and I don't want to generate new passwords for every user and mail them the new password.
How can I implement default membership password hashing/encryption in my provider. I used reflector/googled and tried the following code which seems to be the default implementation in SqlMembershipProvider:
internal static string GenerateSalt()
{
byte[] buf = new byte[16];
(new RNGCryptoServiceProvider()).GetBytes(buf);
return Convert.ToBase64String(buf);
}
internal string EncodePassword(string pass, string salt)
{
byte[] bIn = Encoding.Unicode.GetBytes(pass);
byte[] bSalt = Convert.FromBase64String(salt);
byte[] bAll = new byte[bSalt.Length + bIn.Length];
byte[] bRet = null;
Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
HashAlgorithm s = HashAlgorithm.Create("SHA1");
bRet = s.ComputeHash(bAll);
return Convert.ToBase64String(bRet);
}
And used it in my ValidateUser method:
string salt = GenerateSalt();
string pass = EncodePassword(enteredPassword, salt);
bool verificationSucceeded = pass == user.Password;
But the pass returned from EncodePassword is different from the one that the default SqlMembershipProvider has put into database before switching to custom membership. How can I implement the exact password validation/generation like Default SqlMembershipProvider?
Note: since I have not defined any machinekey in my web.config, I'm sure the default provider used Hashed Password Format, not Encrypted or Clear.
Thank you.
Found the solution:
in ValidateUser method, we have to get PasswordSalt from aspnet_Membership table, not generating it!
Dummy question. :|

how to reset & change the hash password in asp.net membership provider in MVC

I came accross the code :
MembershipUser u = Membership.GetUser();
u.ChangePassword(u.ResetPassword(), "Password"); //where will I get the "Password" from
I dont understand how I will
get the client password as the user has forgotten his old password.
I want to add a reset functionality which would generate a random password and
send an email to the particular client which will have the userid and the random generated password. After he/she would be able to change the password.
You can generate a random password like this using the Membership GeneratePassword method
string password = System.Web.Security.Membership.GeneratePassword(14, 0);
If you need to create your own salt and hash a new password, here is an implementation which does much the same as the membership code:
public class Cryptographer : ICryptographer
{
#region ICryptographer Members
public string CreateSalt()
{
byte[] data = new byte[0x10];
new RNGCryptoServiceProvider().GetBytes(data);
return Convert.ToBase64String(data);
}
/// <summary>
/// Hash the password against the salt
/// </summary>
/// <param name="pass">Plain password</param>
/// <param name="salt">Salt string</param>
/// <returns>Encrypted password</returns>
public string HashPassword(string password, string salt)
{
byte[] bytes = Encoding.Unicode.GetBytes(password);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
byte[] inArray = null;
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create(System.Web.Security.Membership.HashAlgorithmType);
inArray = algorithm.ComputeHash(dst);
return Convert.ToBase64String(inArray);
}
#endregion
}
The second paremeter of the ChangePassword method is a string that reprisents the new password you'd like to use for that user.
You can change this to be any string you want, even an auto generated string that you'll email to the user.
UPDATE
To answer your new question, I believe that all hashing of the password etc is handled by the Membership Provider.
If you simply want to reset the users password to a random new value, you might be better using the ResetPassword method instead of ChangePassword.
This will:
Resets a user's password to a new, automatically generated password.

asp.net membership IsApproved false but still allowing login

i have change the default Account Membership provider to set IsApproved to false.
public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
MembershipCreateStatus status;
_provider.CreateUser(userName, password, email, null, null, false, null, out status);
return status;
}
But i then go back to the login page and it allows me to login. Shouldn't it fail login and say that i am not approved ??
EDIT:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(string userName, string email, string password, string confirmPassword, string address, string address2, string city, string state, string homePhone, string cellPhone, string company)
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
if (ValidateRegistration(userName, email, password, confirmPassword))
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(userName, password, email);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuth.SignIn(userName, false /* createPersistentCookie */);
TempData["form"] = Request.Form;
TempData["isActive"] = false;
return RedirectToAction("Create", "Users");
}
else
{
ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View();
}
(it looks like the other copy of this question is going to be closed, so I've copied my answer here)
HttpRequest.IsAuthenticated returns true if HttpContext.User.Identity is not null and it's IsAuthenticated property returns true.
The current identity is set in the FormsAuthenticationModule, but it has nothing to do with your MembershipProvider. In fact, it doesn't even reference it. All it does is check to see if the authentication cookie is still set and is still valid (as is, has not expired).
I think the problem is that you are calling one of the FormsAuthentication methods like RedirectFromLoginPage, which is settings the authentication cookie. If you need to wait until the user is approved, then you need to make sure you are not setting the cookie.
Update
There are no values of MembershipCreateStatus that specify that the user has been created but not approved, so your code is calling FormsAuth.SignIn without actually checking if the user has been approved.
FormsAuth.SignIn just sets the cookie, that's it. It doesn't validate the user or otherwise have any relation to your MembershipProvider. If approval is asynchronous (ie. waiting for a human), then don't automatically log the user in by calling FormsAuth.SignIn.

Resources