Stockio

Argon2 vs bcrypt: Secure Password Hashing and Migration

Implement password storage with Argon2id, maintain bcrypt safely, benchmark resource costs, and upgrade existing hashes during login without exposing plaintext secrets.

By Chidi Nwosu

Backend Engineer ·

On this page

A stolen password database should not immediately reveal every user's password. Password hashing makes offline guessing expensive, but only when the algorithm, parameters, and application behavior work together. A modern hash does not compensate for passwords in logs, unlimited login attempts, or a migration that silently weakens existing credentials.

Separate password hashing from ordinary hashing

A password hash is a deliberately expensive, one-way verifier. It is not encryption, because the application should never need to recover the original password. Fast general-purpose hashes such as SHA-256 are useful elsewhere, but they let attackers test password guesses too cheaply when used alone for password storage.

Use the Password Hash Generator to explore available output formats with invented sample strings. Check which algorithm and options the tool actually exposes; do not assume every generated hash is appropriate for authentication. Never paste real credentials or production password hashes into a utility merely to inspect them.

Password hashing belongs on the trusted server with a maintained implementation. Browser-side transformations do not replace transport security or server-side hashing: an accepted transformed password can itself become a reusable credential. Follow the OWASP Password Storage Cheat Sheet when establishing policy, rather than copying an unexplained cost value from a snippet.

Define who owns algorithm updates, load testing, and emergency response. The code is small; the operational responsibility lasts for the lifetime of the accounts.

Prefer Argon2id for new password databases

Argon2id combines resistance to memory-focused attacks with protections appropriate for password hashing. Its main controls include memory cost, time cost, and parallelism. Increasing them makes guessing more expensive, but also increases the resources your own authentication service needs.

OWASP lists an Argon2id configuration of at least 19 MiB memory, two iterations, and parallelism one among its recommended minimum settings. Treat this as a documented starting point, not an eternal universal optimum. Benchmark stronger settings on the actual deployment hardware and check the current guidance before establishing a production baseline.

A Node.js example using the maintained argon2 package makes the units explicit:

import * as argon2 from "argon2";

const hashOptions = {
type: argon2.argon2id,
memoryCost: 19456, // KiB: 19 MiB
timeCost: 2,
parallelism: 1,
};

export async function hashPassword(password: string) {
return argon2.hash(password, hashOptions);
}

export async function verifyPassword(
encodedHash: string,
password: string,
) {
return argon2.verify(encodedHash, password);
}

The library generates a fresh random salt and stores the salt, algorithm identifier, parameters, and derived hash in the encoded result. Store that complete string. Do not strip its prefix or place it in a database column sized for an older, shorter format.

Use the library's verification function rather than rehashing with a fresh salt and comparing strings. Consult the node-argon2 documentation for supported runtimes, binary installation, and parameter behavior. Confirm native dependencies work in your deployment image before rollout.

Maintain bcrypt without hiding its limitations

bcrypt remains relevant for existing systems, but it is usually not the first choice for a new password database when Argon2id is available. Its cost parameter is logarithmic: increasing the cost by one roughly doubles the key-setup work. That behavior makes casual parameter increases operationally significant.

The most important compatibility issue is its 72-byte limit in common implementations. Bytes are not characters: a Unicode password may reach the limit with fewer than 72 visible characters. Some implementations truncate excess bytes, which can make distinct long passwords equivalent.

For new bcrypt registrations, enforce a documented byte-length policy before hashing, or choose Argon2id instead. Do not silently trim passwords, change their case, or introduce Unicode normalization without an explicit compatibility plan. Verification must preserve the transformation rules under which the account was created.

The bcrypt package documentation explains implementation-specific behavior. If maintaining legacy accounts, identify the precise library and version, not just the algorithm name. Historical implementation bugs and encoding differences can affect migration behavior.

Avoid improvising a prehash scheme to bypass the length limit. Composition can introduce encoding, null-byte, and compatibility mistakes. A reviewed, documented scheme may be possible, but migrating successfully authenticated accounts to Argon2id is generally easier to reason about.

Benchmark authentication as a bounded workload

A hash that takes an acceptable time in an isolated test can still exhaust a service during concurrent logins. Memory cost multiplies across active Argon2 operations, while CPU work competes with the rest of the application. Measure peak concurrency, queueing, and tail latency, not just a single median duration.

Use asynchronous library APIs where supported, but remember that asynchronous does not mean unlimited capacity. Native workers still consume finite resources. Place a bound on simultaneous password operations and make overload behavior explicit rather than allowing an unbounded queue to consume memory.

Apply layered rate limits by account and network signals, with care for shared networks and deliberate account-lockout abuse. Limit request body size before expensive parsing. A reasonable maximum password length can prevent extreme inputs, but should comfortably allow passphrases and must never silently truncate them.

Credential secrecy includes observability. Redact password fields from request logs, error trackers, traces, and analytics. Restrict access to stored hashes and backups. A separate secret pepper can add protection in some threat models, but requires secure storage, rotation planning, and careful recovery design; it is not a substitute for salts.

For adjacent API controls, use the backend security collection. Password storage is one layer of authentication, not the entire authentication system.

Upgrade hashes during successful authentication

Store enough information to recognize each supported scheme. On login, load the account, dispatch verification to the correct library, and check whether a successful verifier needs an upgrade. Only then derive a new Argon2id hash from the supplied password.

Use an atomic update conditioned on the old hash value so concurrent logins or password resets cannot overwrite a newer credential. Do not rehash the stored bcrypt string as if it were the password; that changes the credential semantics and preserves weaknesses in surprising ways.

Accounts that never return cannot be upgraded this way. Keep the old verifier until your retirement policy says otherwise, or require a secure password reset. Treat password-reset tokens separately: they need expiration, single use, and high entropy, not the same user-selected-password workflow.

Return generic authentication failures to reduce account enumeration. For nonexistent accounts, consider a controlled dummy verification path to reduce obvious timing differences, while preserving rate limits and capacity bounds. Test success, wrong passwords, malformed stored hashes, Unicode inputs, and concurrent upgrades.

Keep synthetic test fixtures readable with the JSON Formatter, excluding real secrets and hashes. Record the policy version and benchmark results so future maintainers know why settings were chosen.

Key takeaways

  • Prefer Argon2id for new password storage and benchmark documented settings.
  • Let maintained libraries generate salts and verify encoded hashes.
  • Respect bcrypt's byte limit and legacy encoding behavior.
  • Bound hashing concurrency and keep credentials out of telemetry.
  • Upgrade only after successful verification, using atomic writes.
Featured sponsor

Reach designers & developers at work

Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.

Become a sponsor

Keep reading

Related articles

More in Backend & APIs
Backend & APIs

How to Store API Keys Securely: Secure Token Generation

Generate unpredictable API tokens, store verifiers safely, and build expiration, rotation, and revocation workflows without confusing password hashes with keys.

Includes: Password & hashChidi NwosuChidi Nwosu
VPN & Online Privacy

10 Best Private Browsers and Search Engines

Compare privacy-minded browsers and search engines for daily use, including tracking protections, familiar features, and the compromises to consider before switching.

Includes: Password & hashRavi DesaiRavi Desai
VPN & Online Privacy

8 Best Encrypted Email and Messaging Apps

Compare eight email and messaging services with encryption features, practical trade-offs, and clear advice on choosing the right option for private conversations and everyday correspondence.

Includes: Password & hashRavi DesaiRavi Desai
Smartphones & Tablets

8 Best Phone Settings to Change Right Away

Make your phone safer, quieter, and easier to use with eight practical settings covering screen locks, backups, permissions, notifications, and battery habits.

Includes: Password & hashDaniel KimDaniel Kim
Online Security & Scams

7 Best Antivirus Apps for Windows and Mac

Choose antivirus protection that fits your computer, budget, and comfort level, with practical comparisons of seven established options for Windows and Mac.

Includes: Password & hashRavi DesaiRavi Desai
Online Security & Scams

6 Best Authenticator Apps for Two-Factor Login

Compare six authenticator apps for stronger account logins, with clear advice on backups, phone changes, recovery codes, and choosing a setup you can maintain.

Includes: Password & hashRavi DesaiRavi Desai