How to Protect Passwords with Argon2 Hashing
Securing user credentials is a critical responsibility in modern web development, requiring approaches that render stored credentials useless if a database breach occurs. Modern web engineers rely on specialized, slow key derivation functions rather than general-purpose cryptographic hash functions to protect user passwords. This article breaks down how developers utilize Argon2—the industry standard hashing algorithm—to neutralize brute-force attempts, GPU-accelerated cracking, and side-channel attacks by configuring memory, time, and parallelism parameters.
The Flaw of Traditional Hashing
In the past, developers frequently relied on algorithms like MD5, SHA-1, or standard SHA-256 to hash passwords. While these algorithms are cryptographically secure for integrity verification, they are intentionally designed to be fast and computationally lightweight.
Modern GPUs and specialized ASIC hardware can calculate billions of SHA-256 hashes per second. If an attacker acquires a database dump containing fast hashes, they can rapidly run dictionary and brute-force attacks to reverse them. To secure credentials effectively, hashing must be computationally expensive.
The Shift to Memory-Hard Functions and Argon2
To counter modern hardware attacks, the Password Hashing Competition (PHC) selected Argon2 as the open standard in 2015. Unlike older adaptive algorithms like bcrypt or PBKDF2, which primarily rely on CPU time to slow down attackers, Argon2 is a memory-hard algorithm.
Argon2 requires a configurable amount of physical RAM to compute a hash. Because GPUs and custom ASICs have limited memory per execution core, forcing the hashing process to consume significant memory makes mass-parallel password cracking economically and physically unfeasible.
Argon2 exists in three variants:
- Argon2d: Maximizes resistance to GPU-cracking attacks using data-dependent memory access. However, it is vulnerable to side-channel timing attacks, making it suitable primarily for cryptocurrencies, not backend web authentication.
- Argon2i: Uses data-independent memory access to eliminate side-channel vulnerabilities, making it resistant to timing attacks.
- Argon2id: A hybrid implementation that combines the approaches of both variants. It uses data-independent addressing for the first pass over memory to thwart side-channel attacks, followed by data-dependent addressing to resist GPU/ASIC attacks. Argon2id is the standard recommendation for web authentication.
Key Parameters Configured by Engineers
When implementing Argon2id, developers configure three distinct cost factors based on their server capacity and response time targets:
- Memory Cost (
m): The amount of RAM allocated to computing a single hash (e.g., 64 MB). Higher memory requirements directly restrict an attacker's ability to run parallel attacks on GPUs. - Time Cost (
t): The number of iterations or passes over the allocated memory. Increasing iterations increases CPU computation time without requiring more memory. - Parallelism (
p): The number of concurrent execution threads utilized to compute the hash.
Engineers benchmark their production hardware to ensure that computing a single hash takes between 250 to 500 milliseconds. This duration is imperceptible to a human logging in via a web form, but completely impractical for an attacker attempting billions of guesses.
The Standard Implementation Lifecycle
Securing credentials with Argon2 follows a defined pattern across registration, storage, and verification:
1. Generating Salts
For every new password, the system generates a cryptographically
secure, random salt (minimum 16 bytes) using system entropy (such as
crypto.randomBytes in Node.js or secrets in
Python). Salts guarantee that two identical passwords never produce
identical hashes, neutralizing precomputed rainbow table attacks.
2. Encoding and Storage
The application passes the plaintext password, salt, and cost parameters to the Argon2id engine. The output is a standardized string following the modular crypt format:
$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ...$R2VuZXJhdGVkSGFzaA...
This single string contains:
- The algorithm variant (
argon2id) - The algorithm version (
v=19) - The memory, time, and parallelism parameters
(
m=65536,t=3,p=4) - The unique salt
- The resulting cryptographic hash
This encoded string is stored directly in the database's password column.
3. Verification and Constant-Time Comparison
When a user attempts to log in:
- The server fetches the user's encoded hash string from the database.
- The verification library extracts the original cost parameters and salt from that string.
- The incoming plaintext password is processed using those extracted parameters.
- The system compares the newly calculated hash against the stored hash using a constant-time comparison algorithm. This ensures the execution time is identical whether the password matches or fails on the first byte, mitigating timing attacks.
Maintaining Long-Term Security
As computing hardware advances, previously secure cost parameters become easier to crack. To handle this, engineers implement automatic re-hashing logic.
During successful user logins, the system checks if the stored hash parameters match the application's current security baseline. If the stored hash was generated with older, weaker parameters, the application automatically computes a new hash using the modern cost factors and updates the database record without disrupting the user experience.