Menu

Is SHA-256 Alone Sufficient for Password Security?

Is SHA-256 Alone Sufficient for Password Security?

You've built a sleek new web application with user accounts, and now you need to store passwords. Like any security-conscious developer, you know never to store passwords in plain text. 😅

After a quick search, you discover SHA-256 — a modern cryptographic hash function from the respected SHA-2 family. It produces a 256-bit hash, seems widely used, and is built right into your framework. Problem solved, right?

Not so fast.

While SHA-256 is an excellent cryptographic hash function for many purposes, using it alone for password storage can be a mistake that could put your users at risk.

This article will explain why SHA-256 by itself fails to provide adequate password security, what attackers can do to exploit these weaknesses, and how to implement proper password security in modern applications.

Understanding SHA-256

SHA-256 is part of the SHA-2 (Secure Hash Algorithm 2) family developed by the NSA and published by NIST back in 2001. It can take input of any size and it produces a fixed 256-bit (or 32 byte) output hash.

This is how it works:

  1. Padding the input message to a multiple of 512 bits
  2. Breaking the padded message into 512-bit blocks
  3. Processing each block sequentially through a compression function
  4. Producing a final 256-bit hash value

The process creates a deterministic, seemingly random output that is computationally difficult to reverse.

Why SHA-256 Alone Isn't Enough

Back in 2008 when I was getting started professionally in the software development field, it wasn't uncommon to see SHA-256 used for password hashing at large organizations.

However, alot has changed during the past few decades, such as the advent of super powerful GPU's that can compute billions of hashes per second.

Despite SHA-256's main strengths, it's got a few characteristics that make it unsuitable as a standalone hashing option, particularly for passwords.

SHA-256 Is Designed to be Fast

SHA-256 was mainly designed for efficiency and speed. This makes it perfect for validating file integrity or digital signatures, but becomes a weakness for password hashing.

Modern GPU's can calculate billions of SHA-256 hashes per second. An NVIDIA RTX 3090 GPU for example can compute over 2 billion SHA-256 hashes per second, making brute force attacks possible.

Just for comparison sake, here's an estimate showing where it lies in terms of speed:

Algorithm Hashes/second (RTX 3090)
SHA-256 2,000,000,000
SHA-256 (salted) 1,800,000,000
PBKDF2-SHA256 (310,000 iterations) 5,800
Bcrypt (cost=12) 23,000
Argon2id (t=3, m=4096, p=1) 3,500

It's a rough estimate, but it paints the picture as to why solely relying on SHA-256 for password hashing is not ideal.

In comparison, something like the more commonly Bcrypt makes way more sense in terms of security.

No Build-in Salt Mechanism

SHA-256 doesn't include any built in method for incorporating a unique salt per password. Without a salt present identical passwords will hash to identical values. This makes certain attacks possible, such as:

  • Rainbow table attacks
  • Cross-user attacks
  • Batch cracking

No Work Factor Adjustment

Unlike modern password hashing functions, SHA-256 lacks a configurable work factor that can be increased over time as hardware becomes more powerful. This means that as computing power increases year after year, SHA-256 becomes increasingly more vulnerable with no real way to compensate.

Parallel Processing Vulnerability

SHA-256's algorithm is highly parallelizable, meaning attackers can distribute cracking attempts across multiple processors, GPU's or even specialized ASICs to speed up attacks.

And if you haven't heard of the term ASIC's before:

Application-Specific Integrated Circuits (ASICs) are custom-designed chips created for a particular use rather than for general-purpose functionality. Unlike general processors like CPUs or even GPUs, ASICs are hardwired to perform specific tasks extremely efficiently.

Proper Password Hashing

Let's take a look at a few key components of secure password storage and hashing that you should be leveraging:

1. Salting

A salt is a random value generated for each user and combined with their password before hashing. A proper salt requires:

  • Uniqueness: A new random salt string for every user and password
  • Sufficient length: At least 16 bytes of randomness
  • Proper generation: Cryptographically secure random number generator
  • Storage strategy: Cryptographically secure random number generator the salt alongside the hash, typically concatenated or in a separate field.

This helps to ensure that no user ever has the same password hash, even when they share the same passwords:

** Example

User 1: helloWorldGTASA2342

User 2: helloWorldLJLJJEIO42

In this example, two users could be sharing the helloWorld password, but because of the added salt, their final hashes will be different.

2. Key Stretching

This technique applies the hash function multiple times in a loop (iteration), making the hashing process slower (on purpose).

  • Iteration count: Modern recommendations suggest 300,000+ iterations for PBKDF2-SHA256.
  • Adaptive costs: Increase iterations over time as hardware improves.
  • Diminishing returns: Each doubling of iterations doubles the attack cost.

3. Memory Hardness

The newest password hashing functions also require significant memory to compute, making attacks using specialized hardware much more expensive in terms of brute forcing:

  • Memory costs: Forces each hashing operation to use a configurable amount of RAM.
  • Time-memory tradeoff: Prevents shortcuts that reduce memory usage.

It's important to note that the more secure techniques purposely rely on being slow and taking up more resources, which again, SHA-256 is not great at.

Better alternatives

So what's a developer to do? Well lucky for us, there are better alternatives that are relatively simple to implement.

PBKDF2

Password-Based Key Derivation Function 2 applies a pseudorandom function many times to derive a key.

The following is a C# example:

// C# implementation using PBKDF2
public static string HashPassword(string password, byte[] salt, int iterations = 310000)
{
    using (var pbkdf2 = new Rfc2898DeriveBytes(
        password, 
        salt, 
        iterations, 
        HashAlgorithmName.SHA256))
    {
        byte[] hash = pbkdf2.GetBytes(32);
        
        // Combine salt, iterations, and hash for storage
        // Format: iterations:salt:hash
        return $"{iterations}:{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}";
    }
}

** Pros:

  • It's built into most programming languages and frameworks
  • It's FIPS-140 compliant
  • Can use different hash functions (SHA-256, SHA-512, etc)

** Cons

  • No memory-hardness (makes it vulnerable to specialized hardware)
  • Requires very high iteration counts for security
  • Parallelizable (making GPU attacks more efficient)

Bcrypt

Bcrypt was developed in 1999 specifically for password-hashing. It derives from the Blowfish cipher and it includes built-in salting. And it's pretty easy to implement:

// C# implementation using BCrypt.Net-Next package
public static string HashPassword(string password)
{
    // WorkFactor 12 is the default - increase for more security
    return BCrypt.HashPassword(password, workFactor: 12);
}

** Pros

  • Purposefully built for password hashing
  • Integrated salt generation and storage
  • Configurable work force
  • It's inherently resistant to GPU acceleration

** Cons

  • Limited to 72 bytes of input
  • Fixed memory usage (not memory-hard)
  • Implementation inconsistencies across language

Argon2id

Argon2id won the 2015 Password Hashing Competition. It combines time, memory and parallelism costs.

// C# implementation using Konscious.Security.Cryptography package
public static string HashPassword(string password)
{
    var salt = new byte[16];
    using (var rng = RandomNumberGenerator.Create())
    {
        rng.GetBytes(salt);
    }
    
    using (var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password)))
    {
        argon2.Salt = salt;
        argon2.DegreeOfParallelism = 4; // Number of threads
        argon2.Iterations = 3;          // Time cost
        argon2.MemorySize = 65536;      // Memory cost in KiB (64 MB)
        
        byte[] hash = argon2.GetBytes(32);
        return $"$argon2id$v=19$m=65536,t=3,p=4${Convert.ToBase64String(salt)}${Convert.ToBase64String(hash)}";
    }
}

** Pros

  • State of the art security design
  • Configurable memory usage, time cost and parallelism
  • Resistant to all known attack vectors
  • Excellent time-memory tradeoff protections

** Cons

  • Less widespread adoption
  • Fewer mature library implementations
  • Less battle-tested than Bcrypt or PBKDF2

Conclusion and Key takeaways

SHA-256 alone is insufficient for password storage in this day and age. Instead:

  • Use a dedicated password function, like Bcrypt or Argon2id.
  • Apply proper salting for each password generated
  • Add additional security layers, such as:
    • Strong password policies
    • Multiple factor authentication
    • Account lockout policies
    • Break detection and notification

It's important for developers to learn how to implement multiple layers of security as statistically, breaking through any one layer is going to happen eventually. But breaking through 3 or 4 is much less likely.

Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.
AD: "Heavy scripts slowing down your site? I use Fathom Analytics because it’s lightweight, fast, and doesn’t invade my users privacy." - Get $10 OFF your first invoice.

Community Comments

No comments posted yet

Code Your Own Classic Snake Game – The Right Way

Master the fundamentals of game development and JavaScript with a step-by-step guide that skips the fluff and gets straight to the real code.

Ad Unit

Current Poll

Help us and the community figure out what the latest trends in coding are.

Total Votes:
Q:
Submit

Add a comment