Creating a Secure Password Storage System in ASP.NET Web Forms with SQL

What is Password Hashing?

Password hashing is the process of converting a user's plain-text password into an irreversible and fixed-length string of characters. This string, known as the password hash, is then stored in the database instead of the actual password.

Why Use Password Hashing?

Storing plain-text passwords in the database is a significant security risk. In case of a data breach, the attackers can easily retrieve the passwords, potentially compromising user accounts on other websites if users reuse passwords. By using password hashing, even if the hashed passwords are exposed, it is computationally infeasible to reverse the process and obtain the original password.

Step-by-Step Implementation


Database Setup

CREATE TABLE Users (
    ID INT PRIMARY KEY IDENTITY(1,1),
    Email NVARCHAR(100) NOT NULL,
    Password NVARCHAR(64) NOT NULL,
    Salt NVARCHAR(16) NOT NULL
);

Registration Process

Create a new ASP.NET Web Forms page named "RegisterUser.aspx" with the following markup.

<h2>User Registration</h2>
<div>
    <label>Email:</label>
    <asp:TextBox ID="txtEmail" runat="server"></asp:TextBox>
</div>
<div>
    <label>Password:</label>
    <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
</div>
<div>
    <asp:Button ID="btnRegister" runat="server" Text="Register" OnClick="btnRegister_Click" />
</div>

Paste the below code in RegisterUser.aspx.cs.

using System;
using System.Data.SqlClient;
using System.Security.Cryptography;
using System.Text;
public partial class RegisterUser : System.Web.UI.Page
{
    // Connection string to your SQL Server database
    private readonly string connectionString = "Data Source=desktop-nabjbko\\sqlexpress;Initial Catalog=dbEmp;Integrated Security=True";
    
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void btnRegister_Click(object sender, EventArgs e)
    {
        
        // Get user input from form
        string email = txtEmail.Text;
        string password = txtPassword.Text;

        // Generate a random salt for each user
        string salt = GenerateSalt();

        // Combine the password and salt and then hash
        string hashedPassword = HashPassword(password, salt);

        // Store the user information in the database
        if (fnRegisterUser(email, hashedPassword, salt))
        {
            // Registration successful
            Response.Write("Registration successful!");
        }
        else
        {
            // Registration failed
            Response.Write("Registration failed.");
        }
    }

    protected string GenerateSalt()
    {
        // Generate a random salt (you can use a cryptographically secure random number generator)
        // For simplicity, we are using a simple random string generator here
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        var saltChars = new char[16];
        for (int i = 0; i < saltChars.Length; i++)
        {
            saltChars[i] = chars[random.Next(chars.Length)];
        }
        return new string(saltChars);
    }

    protected string HashPassword(string password, string salt)
    {
        // Combine the password and salt
        string combinedPassword = password + salt;

        // Choose the hash algorithm (SHA-256 or SHA-512)
        using (var sha256 = SHA256.Create())
        {
            // Convert the combined password string to a byte array
            byte[] bytes = Encoding.UTF8.GetBytes(combinedPassword);

            // Compute the hash value of the byte array
            byte[] hash = sha256.ComputeHash(bytes);

            // Convert the byte array to a hexadecimal string
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                result.Append(hash[i].ToString("x2"));
            }

            return result.ToString();
        }
    }

    protected bool fnRegisterUser(string email, string hashedPassword, string salt)
    {
        try
        {
            using (SqlConnection con = new SqlConnection(connectionString))
            {
                con.Open();
                string query = "INSERT INTO Users (Email, Password, Salt) VALUES (@Email, @Password, @Salt)";
                using (SqlCommand cmd = new SqlCommand(query, con))
                {
                    cmd.Parameters.AddWithValue("@Email", email);
                    cmd.Parameters.AddWithValue("@Password", hashedPassword);
                    cmd.Parameters.AddWithValue("@Salt", salt);
                    cmd.ExecuteNonQuery();
                }
            }
            return true;
        }
        catch (Exception ex)
        {
            // Handle the exception (log, show user-friendly error, etc.)
            return false;
        }
    }
}

When a user registers, follow these steps:

  • Generate a random salt (e.g., 16 characters).
  • Combine the plain-text password with the salt.
  • Hash the combined string using SHA-256 or SHA-512.
  • Store the hashed password and the salt in the "Users" table.

Login Process

Create a new ASP.NET Web Forms page named "Login.aspx" with the following markup.

<h2>Login</h2>
<div>
    <label>Email:</label>
    <asp:TextBox ID="txtEmail" runat="server"></asp:TextBox>
</div>
<div>
    <label>Password:</label>
    <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
</div>
<div>
    <asp:Button ID="btnLogin" runat="server" Text="Login" OnClick="btnLogin_Click" />
</div>

 Now, in the code-behind file "Login.aspx.cs," add the following C# code to handle the login process.

using System;
using System.Data.SqlClient;
using System.Security.Cryptography;
using System.Text;

public partial class Login : System.Web.UI.Page
{
    // Connection string to your SQL Server database
    private readonly string connectionString = "Data Source=YourServer;Initial Catalog=YourDatabase;Integrated Security=True";

    protected void Page_Load(object sender, EventArgs e)
    {
    }

    protected void btnLogin_Click(object sender, EventArgs e)
    {
        // Get user input from form
        string email = txtEmail.Text;
        string password = txtPassword.Text;

        // Check if the entered login credentials are valid
        if (IsValidLogin(email, password))
        {
            // Successful login
            Response.Write("Login successful!");
        }
        else
        {
            // Invalid login
            Response.Write("Invalid login credentials.");
        }
    }

    protected bool IsValidLogin(string email, string enteredPassword)
    {
        try
        {
            // Retrieve the hashed password and salt from the database based on the provided email
            string hashedPasswordFromDatabase;
            string salt;
            using (SqlConnection con = new SqlConnection(connectionString))
            {
                con.Open();
                string query = "SELECT Password, Salt FROM Users WHERE Email = @Email";
                using (SqlCommand cmd = new SqlCommand(query, con))
                {
                    cmd.Parameters.AddWithValue("@Email", email);
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            hashedPasswordFromDatabase = reader["Password"].ToString();
                            salt = reader["Salt"].ToString();
                        }
                        else
                        {
                            // User not found, login failed
                            return false;
                        }
                    }
                }
            }

            // Hash the entered password with the retrieved salt
            string enteredPasswordHash = HashPassword(enteredPassword, salt);

            // Compare the hashed passwords
            return string.Equals(hashedPasswordFromDatabase, enteredPasswordHash);
        }
        catch (Exception ex)
        {
            // Handle the exception (log, show user-friendly error, etc.)
            return false;
        }
    }

    protected string HashPassword(string password, string salt)
    {
        // Combine the password and salt
        string combinedPassword = password + salt;

        // Choose the hash algorithm (SHA-256 or SHA-512)
        using (var sha256 = SHA256.Create())
        {
            // Convert the combined password string to a byte array
            byte[] bytes = Encoding.UTF8.GetBytes(combinedPassword);

            // Compute the hash value of the byte array
            byte[] hash = sha256.ComputeHash(bytes);

            // Convert the byte array to a hexadecimal string
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                result.Append(hash[i].ToString("x2"));
            }

            return result.ToString();
        }
    }
}

In this code, the login process is handled in the btnLogin_Click event handler. The entered email is used to retrieve the corresponding hashed password and salt from the database. The entered password is then hashed using the retrieved salt and compared with the stored hashed password to determine if the login is valid.

Conclusion

By using password hashing with salting, we can significantly enhance the security of our password storage system. It protects users from the consequences of a potential data breach and ensures that their passwords remain secure and confidential.