How to Store Password in Hash Format in ASP.NET Core Web API

How to Store Password in Hash Format in ASP.NET Core Web API

In this article, I will discuss how to Store a Password in Hash Format in the ASP.NET Core Web API Application and show you the different Cryptographic algorithms for storing the Password hash in the database using Entity Framework Core.

What is Password in Hash Format?

A Password in Hash Format refers to the output produced by applying a cryptographic hash function (HMACSHA256 or HMACSHA512) to a password. Cryptographic hash functions are designed to take an input (or ‘message’) and return a fixed-size string of bytes. The output, known as the hash, is typically a string of hexadecimal characters that appears random.

The following are the key characteristics of hashed passwords:

  • The same password will always produce the same hash when the same hash function and the same salt (if used) are applied.
  • Regardless of the length of the original input (password), the hash output will have a fixed length, which depends on the hash function used (e.g., SHA-256 always produces a 256-bit hash).
  • Even a small change in the password will result in a completely different hash, which makes hashes useful for ensuring data integrity.
  • Hash functions are quick to compute, which makes them useful in systems that need to validate passwords rapidly.
Example to Understand Password in Hash Format in ASP.NET Core Web API:

Let us see an example to understand how to Store and use password in Hash Format in ASP.NET Core Web API Application. I will show you the use of SHA256 and SHA512 hashing algorithm and then I will show you the differences between these two cryptographic hashing algorithms and when to use which one in real-time application.

Creating a New ASP.NET Core Web API Project:

First, create a new ASP.NET Core Web API Project named PasswordHashingDemo. We are going to use SQL Server as the database where we will store the password in Hash format and Entity Framework Core as the Data access technology. So, please install the following two packages, either using the Package Manager Console or the NuGet Package Manager for the solution.

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
Define User Model

First, create a folder named Models in the Project root directory, where we will create all our Models and DTOs. We need to create a user model with ID, Username, and PasswordHash properties. So, create a class file named User.cs within the Models folder and then copy and paste the following code:

namespace PasswordHashingDemo.Models
{
    public class User
    {
        public int Id { get; set; }
        public string Username { get; set; }
        public byte[] PasswordHash { get; set; }
        public byte[] PasswordSalt { get; set; }
    }
}
Define UserDTO

Next, create another class file named UserDTO.cs within the Models folder and then copy and paste the following code. This will be our Data Transfer Object, which includes the required properties which are required for user Registration and Login functionalities. The action method of our controller will use this model to get the data from the client.

namespace PasswordHashingDemo.Models
{
    public class UserDTO
    {
        public string UserName {  get; set; }
        public string Password { get; set; }
    }
}
Configure the Database Connection

Instead of hard-coding the connection string with the DbContext class, we will store the connection string in the appsettings.json file. So, add the database connection string within the appsettings.json file as follows:

{
  "ConnectionStrings": {
    "EFCoreDBConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=UserDB;Trusted_Connection=True;TrustServerCertificate=True;"
  }
}
Create Db Context:

Define a context class that inherits from DbContext. So, add a class file named UserDbContext.cs within the Models folder and then copy and paste the following code. Here, you can see we are using DbSet<User> as a property for which the entity framework is going to create a database table named Users, which will mapped to the User entity.

using Microsoft.EntityFrameworkCore;
namespace PasswordHashingDemo.Models
{
    public class UserDbContext : DbContext
    {
        public UserDbContext(DbContextOptions<UserDbContext> options) : base(options) { }

        public DbSet<User> Users { get; set; }
    }
}
Registering the Connection String and DbContext Class:

Next, we need to configure the connection string and register the context class in the Program class. So, modify the Program class as follows. Here, we also tell the Framework to use the property names as defined in the C# model classes.

using PasswordHashingDemo.Models;
using Microsoft.EntityFrameworkCore;
namespace PasswordHashingDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers()
            .AddJsonOptions(options =>
            {
                // This will use the property names as defined in the C# model
                options.JsonSerializerOptions.PropertyNamingPolicy = null;
            });

            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            //Configure the ConnectionString and DbContext class
            builder.Services.AddDbContext<UserDbContext>(options =>
            {
                options.UseSqlServer(builder.Configuration.GetConnectionString("EFCoreDBConnection"));
            });

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();

            app.UseAuthorization();

            app.MapControllers();

            app.Run();
        }
    }
}
Database Migration

Next, we need to generate the Migration and update the database schema. So, open the Package Manager Console and Execute the add-migration and update-database commands as follows. You can give your migration any name. Here, I am giving it Mig1. The name that you are giving it should not be given earlier.

How to Store Password in Hash Format in ASP.NET Core Web API

With this, our Database with Users database table is created, as shown in the below image:

How to Store Password in Hash Format in ASP.NET Core Web API

Implement Password Hashing in ASP.NET Core

For hashing passwords, we can use either HMACSHA256 or HMACSHA512 algorithms. We will create a utility class to handle password hashing. So, create a class file named PasswordHasher.cs and then copy and paste the following code:

namespace PasswordHashingDemo.Models
{
    public static class PasswordHasher
    {
        public static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
        {
            //var hmac = new System.Security.Cryptography.HMACSHA256()
            using (var hmac = new System.Security.Cryptography.HMACSHA512())
            {
                passwordSalt = hmac.Key; // The Key property provides a randomly generated salt.
                passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
            }
        }

        public static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt)
        {
            //var hmac = new System.Security.Cryptography.HMACSHA256(storedSalt)
            using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt))
            {
                var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
                return computedHash.SequenceEqual(storedHash);
            }
        }
    }
}

The above class contains two static methods related to password security: CreatePasswordHash and VerifyPasswordHash. These methods are designed to securely store passwords and verify them using a hash-based message authentication code (HMAC) with the SHA-156 or SHA-512 hashing algorithms.

CreatePasswordHash

This method is used to create a hash and a salt for a given password. This method takes the following three parameters:

  • string password: The plain text password input by the user.
  • out byte[] passwordHash: An output parameter to store the computed hash of the password.
  • out byte[] passwordSalt: An output parameter to store the salt used during the hashing process.
How it works:
  • Initialization of HMACSHA512: An instance of HMACSHA512 is created. When instantiated, this class is automatically provided with a randomly generated key (used as salt).
  • Storing the Salt: The randomly generated key (salt) is stored in the passwordSalt output parameter.
  • Computing the Hash: The password is converted into bytes using the UTF-8 encoding standard, and then it is hashed using the HMACSHA512 instance. The resulting hash is stored in the passwordHash output parameter.
VerifyPasswordHash

This method is used to verify a password against a stored hash and salt. This method takes the following three parameters:

  • string password: The plain text password to verify.
  • byte[] storedHash: The hash stored in the database (or another secure storage) that was previously computed from the password.
  • byte[] storedSalt: The salt stored along with the hash used during the initial hashing process.
How it Works:
  • Initialization of HMACSHA512: An instance of HMACSHA512 is created using the stored salt. The salt helps recreate the same hash from the input password if it is the same as the original.
  • Computing the Hash: The password is again converted into bytes using UTF-8 encoding and hashed with the initialized HMACSHA512 instance.
  • Comparison: The newly computed hash is compared byte-by-byte with the stored hash using the SequenceEqual method from Linq, which ensures that both sequences are identical.
  • Return Value: The method returns true if the password matches the original (i.e. if computedHash equals storedHash); otherwise, it returns false.
Integrating Password Hashing in API

Now, we can use PasswordHasher to hash passwords before storing them. So, create an Empty API Controller named UserController within the Controllers folder and then copy and paste the following code:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PasswordHashingDemo.Models;

namespace PasswordHashingDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly UserDbContext _context;

        public UserController(UserDbContext context)
        {
            _context = context;
        }

        [HttpPost("register")]
        public async Task<ActionResult<User>> Register(UserDTO model)
        {
            byte[] passwordHash, passwordSalt;
            PasswordHasher.CreatePasswordHash(model.Password, out passwordHash, out passwordSalt);

            User user = new User
            {
                Username = model.UserName,
                PasswordHash = passwordHash,
                PasswordSalt = passwordSalt
            };

            await _context.Users.AddAsync(user);
            await _context.SaveChangesAsync();

            //For Demo Purpose we are returning the PasswordHash and PasswordSalt
            return Ok(user);
        }

        [HttpPost("login")]
        public async Task<ActionResult<string>> Login(UserDTO model)
        {
            var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == model.UserName);
            if (user == null || !PasswordHasher.VerifyPasswordHash(model.Password, user.PasswordHash, user.PasswordSalt))
            {
                return Unauthorized("Invalid username or password.");
            }

            // Generate a token or return a success response
            return Ok("User logged in successfully");
        }
    }
}

As you can see, the above Registration action method uses the Password Hasher utility to generate the Password hash and salt and then store them in the database. In this case, if two users try to send the same password, it will create two different password hashes. This is possible because of the dynamically generated salt. On the other hand, the Login action method also uses the Password Hasher utility to generate and compare the password.

With the above changes in place, run the application and test the functionalities, and it should work as expected. For both the action method, you can use the same request body. For example:

{
  "UserName": "Pranaya",
  "Password": "Test@1234"
}

Now, create a few users and then verify the database. You can see for each user, it is creating a different password hash and salt, as shown in the below image:

Why do we need to store the password in a hash format in a database?

Why do we need to store the password in a hash format in a database?

Storing Passwords in hash format in a database is essential for security reasons. The following are the main reasons why this is recommended in Real-time applications:

  • Security Against Data Breaches: If a database is compromised, hashed passwords do not reveal the actual passwords. Hashing algorithms transform the original passwords into a fixed-size string of characters. This means that even if an attacker gains access to the hashed passwords, they cannot easily figure out the original passwords.
  • Prevention of Password Reuse Attacks: Since hashing produces a unique output for each unique input, identical passwords will result in identical hash values. However, with the addition of salt (a random value added to the password before hashing), even identical passwords will result in different hash values, thus enhancing security and preventing attackers from using pre-computed tables to reverse the hashes.
  • Ensuring Data Integrity and Authentication: Hash functions are designed to be sensitive to changes in the input data. Even a small change in the input (e.g., changing one character in the password) results in a significantly different hash. This property helps in verifying the integrity of the data without exposing the actual password.
  • Compliance with Data Protection Regulations: Many data protection standards and regulations require that personal data, particularly passwords, be stored securely. Hashing passwords is considered a best practice in complying with these standards, thereby avoiding legal and reputational risks associated with data breaches.
What are the Differences Between HMACSHA256 and HMACSHA512?

In ASP.NET Core Web API, HMACSHA256 and HMACSHA512 are two of the cryptographic hash functions available for securing data. Both are part of the System.Security.Cryptography namespace. Let us first look at each of these Hash functions, and then we will see the differences between them:

HMACSHA256 in ASP.NET Core:
  • Hash Size: Generates a hash that is 256 bits (32 bytes) long.
  • Security: Provides good security and is widely adopted.
  • Performance: Faster than HMACSHA512 due to its smaller hash size, which can be advantageous in scenarios where performance and lower computational overhead are priorities.
HMACSHA512 in ASP.NET Core:
  • Hash Size: Generates a hash that is 512 bits (64 bytes) long.
  • Security: Offers a higher level of security compared to HMACSHA256 because of its larger hash size.
  • Performance: Generally slower than HMACSHA256 because it deals with larger blocks of data and produces a larger hash. However, on systems with 64-bit processors, the performance difference may be less noticeable.
Choosing Between HMACSHA256 and HMACSHA512
  • Security Needs: If you require higher security and are less concerned about performance, HMACSHA512 might be the better choice. For general purposes, HMACSHA256 provides a balanced approach.
  • Performance Considerations: In performance-sensitive applications, the quicker computation of HMACSHA256 might be more suitable.
  • Compliance Requirements: Some regulations or industry standards might specify the use of a particular hash function size.

In the next article, I will discuss how to Implement HMAC Authentication in an ASP.NET Core Web API Application. In this article, I explain how to Store a Password in Hash Format in an ASP.NET Core Web API Application with examples. I hope you enjoy this article.

Leave a Reply

Your email address will not be published. Required fields are marked *