php 46 lines · 8 steps

Secure password hashing with Argon2id in PHP

A service that hashes passwords with Argon2id and verifies logins while resisting timing attacks and transparently upgrading old hashes.

Explained by highlit
1<?php
2 
3namespace App\Security;
4 
5use App\Repository\UserRepository;
6 
7final class PasswordService
8{
9 private const ALGO = PASSWORD_ARGON2ID;
10 
11 private const OPTIONS = [
12 'memory_cost' => 65536,
13 'time_cost' => 4,
14 'threads' => 2,
15 ];
16 
17 public function __construct(private UserRepository $users)
18 {
19 }
20 
21 public function hash(string $plain): string
22 {
23 return password_hash($plain, self::ALGO, self::OPTIONS);
24 }
25 
26 public function attempt(string $email, string $plain): ?User
27 {
28 $user = $this->users->findByEmail($email);
29 
30 if ($user === null) {
31 password_verify($plain, '$argon2id$dummyhashtopreventtimingleaks');
32 return null;
33 }
34 
35 if (!password_verify($plain, $user->getPasswordHash())) {
36 return null;
37 }
38 
39 if (password_needs_rehash($user->getPasswordHash(), self::ALGO, self::OPTIONS)) {
40 $user->setPasswordHash($this->hash($plain));
41 $this->users->save($user);
42 }
43 
44 return $user;
45 }
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Centralizing the hash algorithm and its cost parameters as constants keeps hashing and verification consistent across the app.
  2. 2Running a dummy verification when no user is found keeps response time uniform, hiding whether an email exists.
  3. 3Checking password_needs_rehash on each successful login lets you raise security parameters over time without forcing resets.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Secure password hashing with Argon2id in PHP — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code