Kate Ratemo

Password Hashing

Password Hashing

So, What Is Hashing?

Hashing is basically turning your password into a secret code that no one (not even you) can reverse.

When someone signs up, you don’t store their actual password. Instead, you store this code, called a hash. Later, when they log in, you hash the password they enter and check if it matches the stored hash. If it does, they’re in.

Wanna see how it works? Try this simple example yourself and see what output you get!

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// This is a C# example you can run in LINQPad.
// It hashes a password using SHA-256 and prints the hash.
// Notice you can choose to keep or remove the hyphens in the output.

void Main()
{
    var password = "MyAwesomePass123";

    // Convert the password string to bytes
    var passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);

    // Create SHA256 instance
    using var sha256 = System.Security.Cryptography.SHA256.Create();

    // Compute the hash
    var hashBytes = sha256.ComputeHash(passwordBytes);

    // Convert hash bytes to a hex string with hyphens (default)
    var hashWithHyphens = BitConverter.ToString(hashBytes);

    // Or convert to hex string without hyphens (more common)
    var hashWithoutHyphens = hashWithHyphens.Replace("-", "").ToLower();

    Console.WriteLine($"Original Password: {password}");
    Console.WriteLine($"SHA-256 Hash (with hyphens): {hashWithHyphens}");
    Console.WriteLine($"SHA-256 Hash (without hyphens): {hashWithoutHyphens}");
}

Here is what I get when I run it

hash


Hashing Alone Is NOT Enough though

Imagine two users both use the same password ,say, 123456. If you only hash the password, they’ll end up with the same hash in the database. You do not want that

Salting

A salt is a random string added to your password before hashing. It makes every hash unique.

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void Main()
{
    // The password we want to hash
    var password = "MyAwesomePass123";

    // Generate a random salt using a GUID string
    var salt = Guid.NewGuid().ToString();

    // Combine the salt and password before hashing
    var saltedPassword = salt + password;

    // Create a SHA256 hashing object
    using var sha256 = System.Security.Cryptography.SHA256.Create();

    // Convert the salted password into bytes
    var bytes = System.Text.Encoding.UTF8.GetBytes(saltedPassword);

    // Compute the SHA256 hash of the salted password bytes
    var hashBytes = sha256.ComputeHash(bytes);

    // Convert the hash bytes to a hexadecimal string with hyphens
    var hash= BitConverter.ToString(hashBytes).Replace("-", "").ToLower();


    // Output the salt and the final hash to the console
    Console.WriteLine("Salt: " + salt);
    Console.WriteLine("Salted Hash: " + hash);
}

Output

saltedhas

Just remember to store both the salt and the hash in your database.

Use Password-Safe Hashing Algorithms

Not all hashing algorithms are built for securing passwords. Some, like SHA-256, are way too fast ,making it easier for attackers to guess passwords quickly.Here are the most secure and recommended hashing algorithms for passwords:

  • bcrypt – simple, trusted, and has a built-in salt
  • PBKDF2 – supported natively in .NET
  • Argon2 – modern, secure, and recommended for new projects

These algorithms are designed to be slow and memory-hungry on purpose. Why? Because it makes brute-force attacks painfully slow for attackers.

Lets try PBKDF2 in .NET

This is short for Password-Based Key Derivation Function 2. It’s a mouthful, but here’s the idea: PBKDF2 takes a password, mixes in a random salt, and hashes it thousands of times to make it computationally expensive for attackers to crack.

.NET gives us a built-in class called Rfc2898DeriveBytes that does all the heavy lifting. With just a few lines of code, you can create a hash that’s strong, slow (on purpose), and safe for storing in a database.

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void Main()
{
    // User's password input
    var password = "MyAwesomePass123";

    // Generate a secure random salt (128 bits)
    var saltBytes = RandomNumberGenerator.GetBytes(16); 
    var saltBase64 = Convert.ToBase64String(saltBytes);

    // Define number of iterations and hash size
    var iterations = 100_000;
    var hashLength = 32; // 256-bit hash

    // Derive the hash using PBKDF2 with SHA256
    var pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, iterations, HashAlgorithmName.SHA256);
    var hashBytes = pbkdf2.GetBytes(hashLength);
    var hashBase64 = Convert.ToBase64String(hashBytes);

    // Output
    Console.WriteLine("Password: " + password);
    Console.WriteLine("Salt (Base64): " + saltBase64);
    Console.WriteLine("PBKDF2 Hash (Base64): " + hashBase64);
    Console.WriteLine("Iterations: " + iterations);
}


Output

salt

Note: When verifying a password, you use the same salt and algorithm to hash the input and compare it with the stored hash.

You notice above that this version is better because:

  • It uses a cryptographically secure random salt (RandomNumberGenerator.GetBytes) instead of a GUID, making it more secure.
  • It encodes the salt and hash in Base64, which is compact and easier to store or transmit.
  • It includes a configurable iteration count with PBKDF2, following best practices to slow down attackers and make it production-ready.

What Should Go in the Database?

You never store the actual password. Here’s a basic table:

Field Description
username Unique name or email
salt Randomly generated per password
password_hash Hashed password (after salting)

If you’re using bcrypt or Argon2, you may not need to store the salt separately because it’s usually bundled in the hash.

Best Practices

  • Never store plain text passwords.
  • Use slow, password-safe algorithms.
  • Generate and store a unique salt for each password.
  • Rely on well-tested cryptographic libraries instead of creating your own.