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.
Related
I need to encrypt credentials in order to connect to a remote server.
From the documentation I found, that the password needs to be hashed like shown below.
MD5(session + MD5(username+ password))
However I wasn't able to get the same hashed password from C#, as sent from the WebClient to the server.
I tried several combination, none of those gave me the same result.
Here is my last approach.
string EncryptPassword(string UserName, string Password, string SessionId)
{
// Password is MD5(sessionId + MD5(login + password))
// Source: https://www.godo.dev/tutorials/csharp-md5/
using (MD5 md5 = MD5.Create())
{
string credentials = $"{UserName}{Password}";
// Hash credentials first
md5.ComputeHash(Encoding.UTF8.GetBytes(credentials));
var inputBuffer = Encoding.UTF8.GetBytes(SessionId).ToList();
inputBuffer.AddRange(md5.Hash);
//var inputBuffer = Encoding.UTF8.GetBytes(SessionId + credentialBuilder.ToString());
md5.ComputeHash(inputBuffer.ToArray());
//md5.TransformBlock(inputBuffer, 0, inputBuffer.Length, inputBuffer, 0);
//md5.TransformFinalBlock(new byte[0], 0, 0);
// Get hash result after compute it
byte[] hashedCredentials = md5
.Hash;
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < hashedCredentials.Length; i++)
{
//change it into 2 hexadecimal digits
//for each byte
strBuilder.Append(hashedCredentials[i].ToString("x2"));
}
return strBuilder.ToString();
}
}
I tried with following test credentials:
username:
login: TestUser
password: TestPassword
session: uu2cO7b7drhxHKItfRpcJ4#qk#230#$R
Valid result: 4dfb147da0d2338cb57e05d1b4b21d07 <= this is how it should like with the infos above.
My result: eb55ef7e70c160ad2dd8fe831a1cf708
Your result requires you to use the textual (hexadecimal) hash result of UserNamePassword, whereas you're using the byte result.
If you replace:
inputBuffer.AddRange(md5.Hash);
with:
string hexHash = BitConverter.ToString(md5.Hash).Replace("-", string.Empty).ToLowerInvariant();
inputBuffer.AddRange(Encoding.UTF8.GetBytes(hexHash));
Try it online
P.S. On the off chance that you're using .NET Core or .NET 5, Microsoft recommend against using SecureString for new development. Source
P.P.S. If you're in control of both ends of this transaction (creating and verifying the hash), I'd suggest changing how you handle the username and password part, as the following will result in the same hash in the first step:
Username: bob Password: bythechickenshed
Username: bobby Password: thechickenshed
please do not mark this as a duplicate because I've already checked multiple posts but none of them helped me.
Asp.net MVC - How to hash password
Hash and salt passwords in C#
I'm building a website that has a login page, and I read this post
https://www.codeproject.com/Articles/704865/Salted-Password-Hashing-Doing-it-Right#normalhashing
on how to make my login secure.
Now I understand that on each sign up I have to create CSPRNGs(Cryptographically secure pseudorandom number generator)
and add it to the password submitted by the user, then hash this mix and store it in the DB, and store the salt for each user because it's a random one for each user. My question is what is the easiest and the most simple way to do that.
In my controller I'm taking the username and the password via this method
public JsonResult Login(LoginModel data)
{
//some code...
//...
}
My login model contain
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
so I'm getting the username and password like (string a = data.Username, ...)
I already added this to my project but I don't know how to continue from here
using System.Security.Cryptography;
I know how to save in the DB for sure I just want to know, how to make a random salt (maybe using RNGCryptoServiceProvider), and how to add it to the user's password, and then hash the final result.
Thanks for any help.
The answer to every "How do I securely hash passwords" question is the same: YOU DON'T. You're in the business of making unique websites, not in the security business. You don't know what you're doing, and you won't recognize if you're doing something wrong. Until your database leaks out in one way or another, and then you're too late to do anything about it, compromising your users' security.
You use well-researched, battle-hardened, maintained code to store user logins. When you're on ASP.NET MVC, you use ASP.NET Identity. You DO NOT roll your own. Period.
You could go about adding salt and hashing the password by using a method such as this. Here we send a method a string. This is basically how entity framework hash's a password when you create a user using the login section.
public static string EncodePassword(string password)
{
byte[] salt;
byte[] buffer2;
if (password == null)
{
throw new ArgumentNullException("password");
}
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
{
salt = bytes.Salt;
buffer2 = bytes.GetBytes(0x20);
}
byte[] dst = new byte[0x31];
Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
return Convert.ToBase64String(dst);
}//Entity Framework default way
The above uses
using System.Security.Cryptography; and system.Buffer
I did this way, and it's working fine.
I added a new class
public class Helper
{
public String CreateSalt(int size)
{
var rng = new RNGCryptoServiceProvider();
var buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
public String GenerateSha256Hash(string input, string salt)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input + salt);
SHA256Managed shaString = new SHA256Managed();
byte[] hash = shaString.ComputeHash(bytes);
return BitConverter.ToString(hash);
}
}
}
And in my controller I added an instance of this class
Helper myHelper = new Helper();
Then I'm passing values to
string salt = myHelper.CreateSalt(5);
string hashedPassword = myHelper.GenerateSha256Hash(*PasswordField*, salt);
hashedPassword = hashedPassword.Replace("-", string.Empty).Substring(0, 16);
I'm removing the "-"from the hashed password and then I'm taking the first 16 characters.
Thanks to #Luke Joshua Park, #gusto2 , #JamesS and #CodeCaster for their help.
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.
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)
My web application logs into a web api. This needs an email and password. I cannot hash these in my database because the api requires the password in plain text.
How can I store my web api credentials in a safer way than plain text, xor, or base64? Is there a 'proper' solution for this sort of thing?
Yes there is, the ProtectedData class, it lets you encrypt a object tied to a windows user acount, so if the user.config file is copied to another user/computer it will not work
In your Settings file, create two string properties named ApiUsername and ApiPassword, then click "View Code at the top and add the following functions
internal sealed partial class Settings {
private MD5 md5 = MD5.Create();
public global::System.Net.NetworkCredential ApiLogin
{
get
{
global::System.Net.NetworkCredential tmp = null;
if (ApiPassword != "")
{
tmp = new System.Net.NetworkCredential();
tmp.UserName = ApiUsername;
try
{
tmp.Password = System.Text.Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(ApiPassword), md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(ApiUsername.ToUpper())), DataProtectionScope.CurrentUser));
}
catch
{
tmp.Password = "";
}
}
return tmp;
}
set
{
global::System.Net.NetworkCredential tmp2 = value;
ApiUsername = tmp2.UserName;
ApiPassword = Convert.ToBase64String(ProtectedData.Protect(System.Text.Encoding.UTF8.GetBytes(tmp2.Password), md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tmp2.UserName.ToUpper())), DataProtectionScope.CurrentUser));
}
}
}
This will add a accessable property called ApiLogin which will contain a NetworkCredential with the decrpted password, when you save the credentials to the disk it stores it in that encrpted protected form that can't be copied to other users.
If the decryption fails it sets the password to blank in the returned credential. If you want the decrption to work on any useraccount on that single machine change the ProtectionScope to DataProtectionScope.LocalMachine.