Secure Password Hashing in C# & PHP: Argon2 & Bcrypt Implementation Guide

In modern web and software development, securely handling user passwords is crucial to prevent security breaches. Storing plaintext passwords is a critical security risk, and hashing them using robust algorithms is the standard practice. In this article, we will explore the concept of password hashing, the importance of using salts, and how to implement secure password hashing in C# and PHP using Argon2 and bcrypt. We will also cover best practices, common mistakes, and additional security considerations when implementing user authentication in a web application.


Why Hash Passwords?

Storing passwords in plaintext is an enormous security risk. If a database is compromised, attackers will have immediate access to all user credentials. Instead, passwords should be hashed, making them virtually irreversible.

Key Goals of Secure Password Storage:

  1. One-way transformation – A password hash should not be reversible.
  2. Uniqueness – Identical passwords should not yield the same hash.
  3. Resistance to brute-force attacks – The hashing process should be computationally expensive to slow down attackers.
  4. Resistance to precomputed attacks (Rainbow Tables) – Hashing should incorporate unique salts to prevent dictionary-based attacks.
  5. Scalability and adaptability – Algorithms should allow fine-tuning to increase computational difficulty as hardware improves.
  6. Secure verification – User authentication should be implemented in a way that prevents timing attacks and unauthorized access.

What is Salting?

Salting is the process of adding a random, unique string (salt) to each password before hashing it. This ensures that even if two users choose the same password, their stored hashes will be different.

Without salting:

  • SHA-256("password123")ef92b778bafee9a1...
  • Another user with “password123” gets the same hash.

With salting:

  • SHA-256("randomSalt1password123")a1b2c3d4e5...
  • SHA-256("randomSalt2password123")f7g8h9i0j1...

By adding a unique salt, attackers cannot use precomputed hash tables (rainbow tables) to reverse hashes.

Where to Store the Salt?

  • Store the salt alongside the hash in the database (not secret).
  • Do not reuse salts – each password should have a unique salt.
  • Use sufficiently long salts (16 bytes or more recommended).

Implementing Secure Password Hashing

1. Using Argon2 for Password Hashing in C#

Argon2 is the recommended hashing algorithm because it is memory-hard, making it resistant to GPU-based attacks.

Installation

First, install the Konscious.Security.Cryptography library via NuGet:

 dotnet add package Konscious.Security.Cryptography

C# Implementation of Argon2 Password Hashing

using System;
using System.Text;
using Konscious.Security.Cryptography;
using System.Security.Cryptography;

public class Argon2Hasher
{
    public static string HashPassword(string password, out string salt)
    {
        byte[] saltBytes = new byte[16];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(saltBytes);
        }
        salt = Convert.ToBase64String(saltBytes);

        using (var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password)))
        {
            argon2.Salt = saltBytes;
            argon2.DegreeOfParallelism = 4;
            argon2.MemorySize = 65536;
            argon2.Iterations = 4;

            byte[] hashBytes = argon2.GetBytes(32);
            return Convert.ToBase64String(hashBytes);
        }
    }

    public static bool VerifyPassword(string password, string salt, string storedHash)
    {
        byte[] saltBytes = Convert.FromBase64String(salt);

        using (var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password)))
        {
            argon2.Salt = saltBytes;
            argon2.DegreeOfParallelism = 4;
            argon2.MemorySize = 65536;
            argon2.Iterations = 4;

            byte[] hashBytes = argon2.GetBytes(32);
            return Convert.ToBase64String(hashBytes) == storedHash;
        }
    }
}

Using Bcrypt for Password Hashing in C#

Bcrypt is another widely used password hashing algorithm that automatically handles salting.

Installation

 dotnet add package BCrypt.Net-Next

C# Implementation of Bcrypt Password Hashing

using System;
using BCrypt.Net;

public class BcryptHasher
{
    public static string HashPassword(string password, int workFactor = 12)
    {
        return BCrypt.Net.BCrypt.HashPassword(password, workFactor);
    }

    public static bool VerifyPassword(string password, string storedHash)
    {
        return BCrypt.Net.BCrypt.Verify(password, storedHash);
    }
}

Verifying a Password in PHP using Bcrypt

$stmt = $pdo->prepare("SELECT password_hash FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
$user = $stmt->fetch();

if ($user && password_verify($_POST['password'], $user['password_hash'])) {
    echo "Login successful!";
} else {
    echo "Invalid username or password.";
}

Conclusion

Implementing secure password hashing is essential for protecting user data. By using Argon2 or Bcrypt, developers can enhance security against brute-force attacks. In summary:

Argon2id is the most secure algorithm, ideal for modern applications.

Bcrypt is still a strong option, offering automatic salting.

Salting ensures uniqueness, preventing precomputed attacks.

Never store plaintext passwords—always hash them before saving.

Verify passwords securely by retrieving stored credentials and using proper comparison techniques.

Whether using C# or PHP, implementing secure password hashing correctly is crucial in any authentication system.

Leave a Reply

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