This is how I create a user to seed my database
if (!context.Users.Any())
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
userManager.Create(user, "P#ssw0rd");
context.AspNetUsersExtendedDetails.AddOrUpdate(userExtended);
context.SaveChanges();
}
The issue happens when I try to update my password like this:
var userStore = new UserStore<ApplicationUser>(dbContext);
var userManager = new UserManager<ApplicationUser>(userStore);
var currentPasswordHash = userManager.PasswordHasher.HashPassword(Input.CurrentPassword);
if(user.PasswordHash == currentPasswordHash)
{
var passwordHash = userManager.PasswordHasher.HashPassword(Input.NewPassword);
user.PasswordHash = passwordHash;
dbContext.SaveChanges();
logger.Info(AspNetEventLogs.Update + " Password updated for User: " + user.UserName);
}
else
{
logger.Error(AspNetEventLogs.Error + " Current password incorrect");
}
I cannot get the hashes to match at all. The method that I used to create the user and hash the password is similar. Not sure what else I can do.
If you look at the source code for PasswordHasher.HashPassword you will see this:
using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
{
salt = deriveBytes.Salt;
subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
}
So, a new salt and subkey is generated when you call it. That's why your check would never (as far as what has been proven, that is) return true.
For this specific purpose, PasswordHasher has a VerifyHashedPassword method that can recreate the hash using the stored salt and subkey -- this is what is called when you log in with Identity.
Notice, however, that your method lacks the update of the user's SecurityStamp which has to be updated when the password changes for security purposes.
Further, notice that all of that manual work you are doing was already thought of in the core libraries of Identity and all you have to do is call UserManager.UpdatePasswordAsync which will check the password before using the new one provided.
Related
How do I hash an users input(password) to database and then later read the hashed password during login?
I believe the solution is to hash the password upon register, where the password is saved as hashed inside db. Later upon Login, it should un-hash and compare its password with users password-input.
But I don't know how to do it.
I allowed password to have nvarchar(MAX)in db since hashed password are usually long.
[Required]
[StringLength(MAX, MinimumLength = 3, ErrorMessage = "min 3, max 50 letters")]
public string Password { get; set; }
Register:
[HttpPost]
public ActionResult Register(User user) {
if (ModelState.IsValid) {
var u = new User {
UserName = user.UserName,
Password = user.Password
};
db.Users.Add(u);
db.SaveChanges();
return RedirectToAction("Login");
}
}return View();
}
Login:
public ActionResult Login() {
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(User u) {
if (ModelState.IsValid)
{
using (UserEntities db = new UserEntities()) {
//un-hash password?
var v = db.Users.Where(a => a.UserName.Equals(u.UserName) && a.Password.Equals(u.Password)).FirstOrDefault();
if (v != null) {
return RedirectToAction("Index", "Home"); //after login
}
}
}return View(u);
}
I'm using database first.
You should never need to unhash a password. A cryptographic hash function is supposed to be a one-way operation.
(And that's precisely why it is called hashing and not encrypting. If unhashing passwords was to be a normal procedure in your flow of operations, then it would not be hashing and unhashing, it would be encrypting and decrypting. So, hashing is a different thing from encryption, precisely because unhashing is not supposed to ever happen.)
Hashing provides security, because nobody can steal your user's passwords even if they manage to view the contents of your database.
When the user registers, compute the hash of their password, store the hash in the database, and forget the password forever.
When the user logs in, compute the hash of the password they entered, (forget that password too,) and see if the hash matches the hash stored in the database.
This is the mechanism used by most websites out there, and that's precisely why if you successfully go through the "I forgot my password" procedure, they will still not show you your password: they don't have it; they cannot retrieve it even if they wanted to. Instead, they send you a password reset link.
As for how to compute a hash from a string, the interwebz abound with answers to that question, for example: MD5 (MSDN); SHA-256 (MSDN); SHA-512 (MSDN)
Use the System.Web.Helpers.Crypto NuGet package from Microsoft.
You hash a password like this:
var hash = Crypto.HashPassword("foo");
You verify a password like this:
var verified = Crypto.VerifyHashedPassword(hash, "foo");
When it comes to security don't try to reinvent the wheel. Use Claims based authentication.
If you still must manage usernames and passwords use Hash-based message authentication code (HMAC)
I would also recommend investing sometime and reading Enterprise Security Best Practices. There are already smarter people who solved this problems why reinvent the wheel. And .NET has all the goodies there.
Example below:
using System.Security.Cryptography;
using System.Text;
//--------------------MyHmac.cs-------------------
public static class MyHmac
{
private const int SaltSize = 32;
public static byte[] GenerateSalt()
{
using (var rng = new RNGCryptoServiceProvider())
{
var randomNumber = new byte[SaltSize];
rng.GetBytes(randomNumber);
return randomNumber;
}
}
public static byte[] ComputeHMAC_SHA256(byte[] data, byte[] salt)
{
using (var hmac = new HMACSHA256(salt))
{
return hmac.ComputeHash(data);
}
}
}
//-------------------Program.cs---------------------------
string orgMsg = "Original Message";
string otherMsg = "Other Message";
Console.WriteLine("HMAC SHA256 Demo in .NET");
Console.WriteLine("----------------------");
Console.WriteLine();
var salt = MyHmac.GenerateSalt();
var hmac1 = MyHmac.ComputeHMAC_SHA256(Encoding.UTF8.GetBytes(orgMsg), salt);
var hmac2 = MyHmac.ComputeHMAC_SHA256(Encoding.UTF8.GetBytes(otherMsg), salt);
Console.WriteLine("Original Message Hash:{0}", Convert.ToBase64String(hmac1));
Console.WriteLine("Other Message Hash:{0}", Convert.ToBase64String(hmac2));
NOTE: Salts do not have to be kept secret and can be stored alongside the hash itself. It's to increase security from rainbow table attack.
Please don't post same question twice. Duplicate from here.
I can't seem to find an answer for this anywhere. I'm using a custom membership provider and membership user with my own tables, which is appearing to be far too much hassle than it's worth. When updating a user record it appears that I need to update it's membership user instance as well, otherwise where I'm relying on data from membership user it isn't corresponding to the updated database.
I've created my own update membership user method, as the existing one only accepted it's own MembershipUser class:
public static void UpdateAccountUser(AccountUser accountUser)
{
// Custom MembershipUser
ToMembershipUser user = new ToMembershipUser(
"AccountUserMembershipProvider",
accountUser.FirstName + " " + accountUser.LastName,
accountUser.ID,
accountUser.Email,
"",
"",
true,
false,
DateTime.Now,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue);
// Fill additional properties
user.ID = accountUser.ID;
user.Email = accountUser.Email;
user.FirstName = accountUser.FirstName;
user.LastName = accountUser.LastName;
user.Password = accountUser.Password;
user.MediaID = accountUser.MediaID;
user.Media = accountUser.Media;
user.Identity = accountUser.Identity;
user.CreatedAt = accountUser.CreatedAt;
user.UpdatedAt = accountUser.UpdatedAt;
UpdateCookie(user.Email);
}
private static void UpdateCookie(string email)
{
HttpCookie cookie = FormsAuthentication.GetAuthCookie(email, true);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
// Store UserData inside the Forms Ticket with all the attributes
// in sync with the web.config
var newticket = new FormsAuthenticationTicket(ticket.Version,
ticket.Name,
ticket.IssueDate,
ticket.Expiration,
true, // always persistent
email,
ticket.CookiePath);
// Encrypt the ticket and store it in the cookie
cookie.Value = FormsAuthentication.Encrypt(newticket);
cookie.Expires = newticket.Expiration.AddHours(24);
HttpContext.Current.Response.Cookies.Set(cookie);
}
Now obviously this is just creating a new instance and not updating the existing one, which doesn't allow me to see the updated details without logging out and logging back in. Any ideas?
Edit
So I managed to find an example of someone else using their own custom update method, but all they appear to be doing is updating the database not the MembershipUser itself. I'm already doing this?!
I attempted to instead just update the FormsAuthenticationCookie and then when calling Membership.GetUser() I at least pass an updated User.Identity.Name, but to no avail, still old data even though database is updated. I've ran out of ideas...
This could possibly be a repeat of yesterday's question MembershipUser not getting updated result from database
I eventually found out the MembershipUser wasn't updating due to every time I calling GetUser() the query involved was using FirstOrDefault(). Turns out this caches the result rather than retrieving a fresh result. By adding AsNoTracking() to the query itself I now get the updated result, without having to even call UpdateUser().
I need to create AD users automatically. The problem is, making sure the generated password meets the password policy of AD. I don't know what the policy will be, is there a way to determine that at runtime? This is what I'm using but you can see the complexity is static to length=16 and 4 non-alphanumeric chars and might not always work. I am looking for a way to get the password policy from AD so the generated passwords are correct.
UserPrincipal up = new UserPrincipal(oPrincipalContext);
up.SamAccountName = userId;
up.SetPassword(System.Web.Security.Membership.GeneratePassword(16, 4));
up.Enabled = false;
up.ExpirePasswordNow();
up.Save();
This is what I'm using. I'm getting the password properties using DirectoryEntry/LDAP, then use those to create the password.
DirectoryEntry child = new DirectoryEntry("LDAP://machine/DC=domain,DC=com");
int minPwdLength = (int)child.Properties["minPwdLength"].Value;
int pwdProperties = (int)child.Properties["pwdProperties"].Value;
Use the properties when creating the password.
UserPrincipal up = new UserPrincipal(oPrincipalContext);
up.SamAccountName = userId;
up.SetPassword(System.Web.Security.Membership.GeneratePassword(minPwdLength,
pwdProperties));
up.Enabled = true;
up.ExpirePasswordNow();
up.Save();
Well I am using DirectoryEntry and LdapConnection to reset password in a scenario where we have password minimum age and history policy enforced. When someone forgets their password, you want them to be able to reset their password to something which doesn't violate password history. As an alternative solution, it would be possible to use "SetPassword" and reset the password to a generated value and then force the user to change it on their next login. This is not possible in our scenario. Hence, I was following this blog post in technet and trying out LDap extended controls to reset password by honoring password history. In brief, it's just changing to the same password again and again without complaints. My code is as follows:
private static void PasswordChanger(DirectoryConnection ldapCon,
string distinguishedName,
string passwordToSet = null)
{
// the 'unicodePWD' attribute is used to handle pwd handling requests
// modification control for the replace operation
var damReplace = new DirectoryAttributeModification
{
Name = "unicodePwd"
};
// value to be send with the request
damReplace.Add(Encoding.Unicode.GetBytes(String.Format("\"{0}\"", passwordToSet)));
// this is a replace operation
damReplace.Operation = DirectoryAttributeOperation.Replace;
// combine modification controls
var damList = new DirectoryAttributeModification[]
{
damReplace
};
// init modify request
var modifyRequest = new ModifyRequest(distinguishedName, damList);
// the actual extended control OID
const string ldapServerPolicyHintsOid = "1.2.840.113556.1.4.2239";
// build value utilizing berconverter
var value = BerConverter.Encode("{i}", new object[] { 0x1 });
// init exetnded control. The variable name represts the actual extended control name.
var LDAP_SERVER_POLICY_HINTS_OID = new DirectoryControl(ldapServerPolicyHintsOid,
value, false, true);
// add extended control to modify request
modifyRequest.Controls.Add(LDAP_SERVER_POLICY_HINTS_OID);
/* send the request into the LDAPConnection and receive the response */
var result = ldapCon.SendRequest(modifyRequest);
}
The call to Password changer is enclosed as follows,
using (var domain = Domain.GetDomain(new DirectoryContext(
DirectoryContextType.DirectoryServer,
ActiveDirectoryInstance,
request.ServiceAccountName,
request.ServiceAccountPassword)))
using (var directoryEntry = domain.GetDirectoryEntry())
using (var directorySearcher = new DirectorySearcher(directoryEntry))
using (var conn = new LdapConnection(new LdapDirectoryIdentifier(ActiveDirectoryInstance),
new NetworkCredential(request.ServiceAccountName,
request.ServiceAccountPassword,
ActiveDirectoryInstance),
AuthType.Ntlm))
{
...
...
PasswordChanger(....)
...
...
}
EDIT:
This is to do with the scenario explained here
https://support.microsoft.com/en-us/kb/2386717/
RE my comment re "As an alternative solution, it would be possible to use "SetPassword" and reset the password to a generated value and then force the user to change it on their next login."
We can't do that in our scenario as we have password history and minimum age restrictions (24h) enabled. Hence I can't use ChangePassword in user context and SetPassword in admin context (as that wouldn't respect password history).
The LDAP_SERVER_POLICY_HINTS_OID control only enforces password history constraints, meaning that you're only preventing password reuse which should be a non-issue if you generate random passwords in the first place.
To test new passwords against password complexity settings, you would need access to the password filters installed on Domain Controllers.
Otherwise you'll need to use SetPassword - it will enforce complexity requirements, but not password history.
I'm writing custom forms authentication for ASP.NET MVC 5 (no, I don't want to use ASP.NET Identity). I'm trying to hash my passwords using a randomly-generated salt and then hashing salt+password using SHA512. Here are the methods I've written:
private static User SetPassword(User newUser, string password)
{
var rand = RNGCryptoServiceProvider.Create();
var saltBytes = new byte[128];
var passwordBytes = Encoding.UTF8.GetBytes(password);
rand.GetNonZeroBytes(saltBytes);
var passHash = SHA512Managed.Create().ComputeHash(saltBytes.Concat(passwordBytes).ToArray());
var hash = Encoding.UTF8.GetString(passHash);
var salt = Encoding.UTF8.GetString(saltBytes);
newUser.PasswordHash = hash;
newUser.Salt = salt;
return newUser;
}
private static bool ValidatePassword(User user, string passwordTry)
{
var actualPasswordBytes = Encoding.UTF8.GetBytes(user.PasswordHash);
var passwordTryBytes = Encoding.UTF8.GetBytes(passwordTry);
var saltBytes = Encoding.UTF8.GetBytes(user.Salt);
var passwordTryHashBytes = SHA512Managed.Create().ComputeHash(saltBytes.Concat(passwordTryBytes).ToArray());
if (passwordTryHashBytes == actualPasswordBytes)
{
return true;
}
return false;
}
If I step through the code, the registration (SetPassword()) method appears to work successfully. The user's record gets set with a UTF-8 encoded password hash and salt.
If I step through the password validation method, everything also appears to be operating normally. The user's record (the salt and hash) are checked against the password try.
The problem is, when I register with a password and then try to log in as that user, the password validation fails. I'm probably not understanding how one of the Cryptography classes works... can anyone explain why this doesn't work?
The following code will always fail because both objects do not point to the same reference:
if (passwordTryHashBytes == actualPasswordBytes)
Try using LINQ's SequenceEqual() method
passwordTryHashBytes.SequenceEqual(actualPasswordBytes)