Secure Authentication with Bcrypt and Argon2 — Password Hashing That Actually Works
> cd .. / HUB_EDITORIALE
Sicurezza Informatica

Secure Authentication with Bcrypt and Argon2 — Password Hashing That Actually Works

[2026-07-30] Author: Ing. Calogero Bono
> share
Zenithby Meteora Web The operating system for your business. Social, clients, bookings and invoices in one platform. Gyms, barbers, professionals. Discover Zenith Free demo · no card

If you're reading this guide, you probably already have a web app with a login form. And you're probably storing passwords in the worst possible way: SHA-1, MD5, or even plaintext. We at Meteora Web see it every day in the security audits we run for clients. They have spent money on design and advertising, but left the back door wide open. A single database leak means thousands of compromised users, legal liability, and shattered trust. All because someone chose the easy path instead of the right one.

This guide covers exactly how to protect passwords using bcrypt and Argon2, and how to manage sessions so that session theft doesn't compromise your entire system. No academic theory: actionable code you can apply today in your Laravel, vanilla PHP, or Node.js project.

Why a simple SHA-256 hash is not enough to protect passwords

Let's address a common misconception: "I use SHA-256 with a salt, so it's safe, right?" The answer is no. SHA-256 is a fast hashing algorithm. Fast for you, but even faster for an attacker with a GPU. A single RTX 4090 can compute billions of SHA-256 hashes per second. Even with a salt, a rainbow table adapted to your algorithm cracks most passwords within hours.

Sponsored Protocol

The key difference between a fast hash and a slow one is the cost factor. Bcrypt and Argon2 are deliberately designed to be slow: they increase the computation time. A legitimate login attempt takes 100-200 milliseconds. For an attacker trying millions of combinations, that time becomes prohibitively expensive.

We at Meteora Web have audited companies with hundreds of thousands of users still using SHA-1 because "nobody attacks us". Then a credential stuffing attack hit and they lost everything. Security is not optional—it's part of the product.

How does bcrypt compare to Argon2?

Bcrypt has been the de facto standard for years. It is based on Blowfish and uses a configurable cost (the "rounds" parameter). It is CPU-bound: the higher the cost, the slower it runs. It works well on normal hardware, but has a limitation: it doesn't use memory, so it is vulnerable to GPU parallel attacks if the cost is low.

Argon2 is the winner of the Password Hashing Competition (PHC) from 2015. It comes in three variants: Argon2d (side-channel resistant), Argon2i (timing-attack resistant), and Argon2id (hybrid, recommended). It is memory-hard: it requires a defined amount of RAM in addition to CPU time. This makes it far more resistant to ASIC or GPU attacks.

Sponsored Protocol

Practical choice: if you use PHP 7.2+, Laravel already includes PASSWORD_ARGON2ID in the password_hash function. On older versions or legacy environments, bcrypt is still fine — just raise the cost to 10 or 12. In Node.js, both bcrypt and argon2 are mature npm packages.

We recommend Argon2id if possible, because it offers a good balance between security and performance. But if your hosting has memory limits (e.g., shared servers), bcrypt with cost 10 is still acceptable.

How to configure bcrypt cost in PHP

// Hash with cost 12 (~250ms on modern CPU)
$hash = password_hash('user_password', PASSWORD_BCRYPT, ['cost' => 12]);

// Verify
if (password_verify('submitted_password', $hash)) {
    // Login ok
}

// Best available algorithm (Argon2id on PHP 7.3+)
$hash = password_hash('password', PASSWORD_ARGON2ID, [
    'memory_cost' => 65536, // 64 MB
    'time_cost'   => 4,
    'threads'     => 2
]);

How to automatically upgrade existing password hashes

if (password_verify($password, $storedHash)) {
    if (password_needs_rehash($storedHash, PASSWORD_ARGON2ID, ['memory_cost' => 65536])) {
        $newHash = password_hash($password, PASSWORD_ARGON2ID, ['memory_cost' => 65536]);
        // Save $newHash in database
    }
}

Which implementation should you choose in Laravel for bulletproof security?

Laravel uses bcrypt by default, but you can switch to Argon2ID by editing config/hashing.php. Here's the configuration we use in our projects:

Sponsored Protocol

'driver' => env('HASHING_DRIVER', 'argon2id'),
'argon2id' => [
    'memory' => 65536,
    'time'   => 4,
    'threads' => 2,
],

Don't forget to set HASHING_DRIVER=argon2id in your .env. Then all Hash::make() and Hash::check() calls will use Argon2id. For existing passwords, Laravel does not automatically rehash them: you need to implement the upgrade logic as shown above.

How to manage sessions securely

Even with properly hashed passwords, if an attacker steals the session cookie, they can log in without even trying the password. Here are the points we always check in our audits:

Sponsored Protocol

Session ID rotation after login

After a successful login, you must regenerate the session ID. In Laravel, use session()->regenerate(). In vanilla PHP: session_regenerate_id(true). Without this rotation, an attacker who intercepted the pre-login cookie can continue using it after login.

Secure session cookies

The cookie must have HttpOnly (inaccessible by JavaScript), Secure (HTTPS only), and SameSite=Strict or Lax. In Laravel, set SESSION_SECURE_COOKIE=true and SESSION_SAMESITE=lax in your .env. In vanilla PHP:

session_set_cookie_params([
    'lifetime' => 1209600, // 14 days
    'path' => '/',
    'domain' => 'yourdomain.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);

Store sessions in database or Redis

Avoid file-based sessions on shared servers – they can be read by other users on the same system. In Laravel, set SESSION_DRIVER=database or redis. For database, create the table with php artisan session:table and migrate. Also set a short expiration and a cron job to clean up expired sessions.

Sponsored Protocol

Inactivity timeout and forced logout

A user leaving a session open on a public computer is a risk. Implement an inactivity timeout (e.g., 30 minutes) and automatic logout. In Laravel, you can use middleware or event listeners to track last activity.

What to do now

Don't wait until your database is leaked to take action. Here's your operational checklist:

  • Check your current hashing algorithm: if it's not bcrypt or argon2, it's wrong. Change it immediately.
  • Set an adequate cost: for bcrypt at least 10, for argon2id memory 64MB, time 4.
  • Add session ID rotation in your login callback.
  • Configure cookies with HttpOnly, Secure, SameSite.
  • Move sessions from filesystem to database or Redis.
  • Test with password_needs_rehash for gradual upgrade.

We at Meteora Web have secured dozens of projects using these techniques. If you have doubts or want a complete security audit, we start with one question: how much is your users' trust worth to you? For the full picture on web security, read our pillar guide on web security for developers (English version available).

> share
Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()