Quizzr Logo

Password Hashing

Using Argon2id to Defeat GPU and ASIC Cracking Rigs

Deep dive into Argon2id, the current industry gold standard, and how its memory-hardness thwarts attackers using specialized parallel hardware.

SecurityIntermediate15 min read

The Economics of Modern Password Cracking

In the early days of web development, simple hash functions like MD5 and SHA-1 were considered sufficient for credential storage. These algorithms were designed for high-performance data integrity checks, meaning they were optimized to process gigabytes of data per second. This speed, which is a feature for file checksums, becomes a critical vulnerability when applied to password hashing.

An attacker attempting to breach a database does not need to reverse the hash function through complex mathematics. Instead, they use a brute-force approach, testing millions of password combinations until they find a matching output. If a hash function is fast, an attacker can iterate through an entire dictionary of common passwords in mere milliseconds using off-the-shelf consumer hardware.

The fundamental problem is the growing asymmetry between the defender and the attacker. While a developer only needs to verify one password during login, an attacker can leverage massive parallelization. Modern Graphics Processing Units are built to perform thousands of simultaneous operations, making them ideal for cracking simple, fast hash functions.

The goal of a secure password hashing algorithm is not just to obfuscate data, but to make the cost of an attack economically unviable by forcing the attacker to use significant hardware resources for every single attempt.

To combat this, the industry moved toward slow hashes like PBKDF2 and bcrypt. These algorithms introduce a work factor that forces the CPU to perform many iterations, slowing down the hashing process. However, even these have started to show weaknesses as specialized hardware like Application-Specific Integrated Circuits has become more accessible to malicious actors.

The Rise of Specialized Hardware

Application-Specific Integrated Circuits and Field-Programmable Gate Arrays represent a major shift in the threat landscape. Unlike a general-purpose CPU, these devices are custom-built for one specific task, such as computing SHA-256 or bcrypt hashes. This allows them to bypass the inefficiencies of a standard operating system and standard processor architecture.

A single custom chip can perform the work of hundreds of high-end CPUs while consuming a fraction of the power. This efficiency allows attackers to build massive cracking rigs that can test billions of passwords per second. To defend against this, we need an algorithm that requires resources that these specialized chips find difficult to provide in bulk, such as large amounts of memory.

The Argon2id Architecture

Argon2 was the winner of the Password Hashing Competition in 2015 and has since become the recommended standard by organizations like OWASP and IETF. It is designed specifically to be memory-hard, meaning the computation requires a significant amount of RAM to complete. This is a direct countermeasure against specialized hardware, as adding high-speed memory to a custom chip is expensive and difficult to scale.

There are three primary variants of the algorithm: Argon2i, Argon2d, and Argon2id. Argon2i is optimized to resist side-channel attacks by accessing memory in a password-independent order, but it is slightly more vulnerable to certain hardware-based attacks. Argon2d is faster and provides the strongest resistance against hardware attacks by using data-dependent memory access, but it is potentially vulnerable to side-channel timing attacks.

Argon2id is a hybrid approach that provides the best of both worlds for web applications. It uses the side-channel resistant approach of Argon2i for the first pass over memory and the hardware-resistant approach of Argon2d for subsequent passes. This makes it the current industry gold standard for general-purpose credential storage.

  • Memory Cost: Defines the amount of RAM required for the hash calculation.
  • Time Cost: Sets the number of iterations over the memory blocks.
  • Parallelism: Determines how many independent threads the algorithm can use to utilize multi-core processors.
  • Salt: A unique, random string added to each password to prevent the use of precomputed rainbow tables.

By tuning these parameters, developers can ensure that even if a database is leaked, the cost of cracking the passwords would be astronomical. For example, setting a memory cost of 64 megabytes forces an attacker to dedicate that much high-speed RAM to every single parallel cracking attempt. This requirement effectively neutralizes the advantage of high-density hardware rigs.

Memory-Hardness Explained

The concept of memory-hardness is central to why Argon2id is so effective. Traditional algorithms rely on compute cycles, which are cheap and easy to parallelize on a chip. Argon2id creates a large internal state in memory and then performs complex operations that require frequent, unpredictable access to that memory.

If an attacker tries to use less memory than specified, the algorithm forces them to recompute values repeatedly, leading to a massive performance penalty. This trade-off between memory and time ensures that there is no shortcut for the attacker. To compute the hash quickly, they must spend the money on the physical memory modules required by the algorithm.

Practical Implementation and Tuning

Implementing Argon2id should always be done using well-reviewed, high-level libraries rather than attempting a custom implementation. Most modern languages have bindings for the official Argon2 C library, ensuring performance and security. When using these libraries, the focus shifts from the internal math to the proper selection of configuration parameters.

The goal of tuning is to find the maximum possible resource usage that your server can handle without degrading the user experience. A login request should ideally take between 200 and 500 milliseconds. This delay is imperceptible to a human user but acts as a massive bottleneck for an automated attack system.

javascriptNode.js Implementation with argon2
1const argon2 = require('argon2');
2
3async function secureUserRegistration(rawPassword) {
4    try {
5        // Use high-level parameters recommended for modern hardware
6        const hash = await argon2.hash(rawPassword, {
7            type: argon2.argon2id,
8            memoryCost: 65536, // 64 MB of RAM
9            timeCost: 3,       // 3 iterations
10            parallelism: 4     // utilize 4 threads
11        });
12        return hash;
13    } catch (err) {
14        console.error('Hashing failed:', err);
15        throw new Error('Internal security error');
16    }
17}
18
19async function verifyUserLogin(storedHash, providedPassword) {
20    // The verify function extracts parameters and salt from the hash string
21    const isMatch = await argon2.verify(storedHash, providedPassword);
22    return isMatch;
23}

In the example above, the memory cost is set to 64 megabytes. If your production environment is running on small containers with limited RAM, you might need to lower this value. Conversely, if your application runs on high-end dedicated hardware, you should increase these values to maintain a strong security posture against future hardware improvements.

It is also vital to consider the parallelism parameter, which should be set based on the number of CPU cores available to your application process. If you set parallelism to four but only have two cores available, the algorithm will not run more securely; it will simply compete for resources more aggressively. This can lead to thread exhaustion and increased latency during peak traffic periods.

Handling Side-Channel Resistance

Timing attacks are a specific type of side-channel attack where an attacker measures how long it takes for a server to respond to a login attempt. If the verification process exits early when it finds an incorrect character, the attacker can slowly guess the password byte by byte. Argon2id mitigates this by using constant-time comparison methods.

Even when using a secure algorithm, developers must ensure that the rest of the authentication flow is constant-time. This includes looking up the user in the database and returning errors. Always return a generic error message like invalid credentials rather than specifying whether the username or password was incorrect.

Managing Large-Scale Credential Migrations

One of the most common challenges in security engineering is upgrading legacy systems that use outdated hashing methods like SHA-1 or bcrypt. You cannot simply re-hash the old hashes with Argon2id, as this adds a layer of complexity without fixing the underlying weakness of the inner hash. The most effective strategy is a rolling migration that occurs during user login.

When a user logs in, you verify their password using the old algorithm. If the verification succeeds, you immediately re-hash the plaintext password using Argon2id and update the database record. Over time, your active user base will be migrated to the more secure format without requiring a disruptive password reset for everyone.

pythonSeamless Migration Pattern
1def authenticate_and_upgrade(user_record, provided_password):
2    # Check if the stored hash uses the legacy format
3    if user_record.hash_type == 'legacy_bcrypt':
4        if bcrypt.verify(provided_password, user_record.hash_value):
5            # Upgrade to Argon2id immediately
6            new_hash = argon2.hash(provided_password)
7            database.update_user(user_record.id, hash_value=new_hash, hash_type='argon2id')
8            return True
9    
10    # Standard verification for modern hashes
11    elif user_record.hash_type == 'argon2id':
12        return argon2.verify(user_record.hash_value, provided_password)
13    
14    return False

This approach requires maintaining a column in your database to track which algorithm was used for each user. It also allows you to identify users who have not logged in for a long time. These stale accounts can eventually be flagged for a mandatory password reset or handled via a more aggressive offline migration strategy if security policies require it.

During a migration, it is important to monitor server performance closely. Hashing is a CPU-intensive task, and a sudden surge of users migrating to a more resource-heavy algorithm like Argon2id can spike CPU usage. You may need to provision additional capacity or implement rate-limiting to ensure the migration process does not inadvertently cause a denial of service.

Versioning Your Hashes

A best practice for long-term security is to include a version identifier within your stored hash strings. Most modern libraries do this automatically by using a standard format that includes the algorithm name and the parameters used. This ensures that even years from now, your system will know exactly how to verify an old hash.

If the industry eventually moves beyond Argon2id to a newer standard, having versioned hashes will make that transition much smoother. You will be able to support multiple algorithms simultaneously and upgrade users incrementally as they interact with your service. This forward-thinking architecture is a hallmark of high-quality security engineering.

Trade-offs and Operational Security

While Argon2id is technically superior to many alternatives, it introduces operational trade-offs that teams must manage. The high memory usage means that your web servers need more overhead. If you are running dozens of microservices on a single host, the cumulative memory requirement of Argon2id can become a significant factor in your infrastructure costs.

Another risk is the potential for Denial of Service attacks. Since Argon2id is intentionally slow, an attacker could flood your login endpoint with fake requests, forcing your server to spend all its CPU and memory on useless hashing tasks. To mitigate this, you must implement strict rate-limiting and potentially use a CAPTCHA or proof-of-work challenge for suspicious traffic.

Finally, always remember that password hashing is only one piece of the security puzzle. A perfectly hashed password will not protect a user who has been phished or whose session token has been stolen. Comprehensive security requires a multi-layered approach including Multi-Factor Authentication, secure session management, and constant monitoring for unusual patterns.

In summary, Argon2id provides a robust defense against modern hardware-accelerated attacks by being both time-expensive and memory-hard. By understanding the parameters and implementing a clear migration path, software engineers can significantly raise the cost for attackers. Staying current with these cryptographic standards is essential for maintaining trust and protecting user data in an increasingly hostile digital environment.

We use cookies

Necessary cookies keep the site working. Analytics and ads help us improve and fund Quizzr. You can manage your preferences.