Lesson 2
Hashing Passwords During Registration
Hashing Passwords During Registration

Welcome back! In the previous lesson, you learned how to implement user registration in your Symfony MVC application. Today, we'll take user registration a step further by enhancing security through password hashing.

Password security is a critical aspect of any web application. Without proper security measures, user passwords can be at risk of being exposed, leading to potential security breaches. Hashing passwords ensures that even if your database is compromised, the actual passwords remain protected.

Brief Explanation of How Hashing Works

Hashing uses mathematical functions to convert plain text passwords into hashed strings. Unlike encryption, hashing is a one-way process, meaning you cannot retrieve the original password from the hashed string. This one-way property makes hashing ideal for securely storing passwords.

Plain text passwords are exactly as the user enters them—readable and unprotected. Hashed passwords, on the other hand, are irreversible and secure. For example:

  • Plain text: password123
  • Hashed: $argon2id$v=19$m=65536,t=4,p=1$...
Adapting the User Entity

To securely store passwords, we need to implement the PasswordAuthenticatedUserInterface in our User entity and include relevant methods.

Here's the updated User entity:

php
1<?php 2 3namespace App\Entity; 4 5use Doctrine\ORM\Mapping as ORM; 6use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; 7 8#[ORM\Entity(repositoryClass: "App\Repository\UserRepository")] 9#[ORM\Table(name: "users")] 10#[ORM\UniqueConstraint(name: "username_unique", columns: ["username"])] 11class User implements PasswordAuthenticatedUserInterface 12{ 13 #[ORM\Id] 14 #[ORM\GeneratedValue] 15 #[ORM\Column(type: "integer")] 16 private $id; 17 18 #[ORM\Column(type: "string", length: 255, unique: true)] 19 private $username; 20 21 #[ORM\Column(type: "string", length: 255)] 22 private $password; 23 24 // Required by PasswordAuthenticatedUserInterface 25 public function getUserIdentifier(): string 26 { 27 return $this->username; 28 } 29 30 // Getters and setters... 31}

In this updated User entity, we implement the PasswordAuthenticatedUserInterface which requires the getUserIdentifier method.

Why is this Necessary?

The PasswordAuthenticatedUserInterface is part of Symfony's security framework and it standardizes how user entities handle password-based authentication. When you implement this interface, Symfony knows that your User entity supports password hashing and authentication.

The getUserIdentifier method is crucial because it returns a unique identifier for the user—in this case, the username. This unique identifier is used by Symfony’s password hasher to create a secure, hashed version of the user's password.

By ensuring our User entity implements this interface and method, we make it fully compatible with Symfony's security mechanisms, allowing us to leverage built-in tools like UserPasswordHasherInterface to securely hash and store user passwords. This compliance ensures our application follows best practices for security.

Adapting Our User Service to Handle Password Hashing

To hash the passwords when users are registering, we need to modify our UserService to use Symfony’s UserPasswordHasherInterface. We'll start by injecting this interface into the UserService.

php
1<?php 2 3namespace App\Service; 4 5use App\Entity\User; 6use App\Repository\UserRepository; 7use Doctrine\ORM\EntityManagerInterface; 8use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; 9 10class UserService 11{ 12 private $entityManager; 13 private $userRepository; 14 private $passwordHasher; 15 16 public function __construct(EntityManagerInterface $entityManager, UserRepository $userRepository, UserPasswordHasherInterface $passwordHasher) 17 { 18 $this->entityManager = $entityManager; 19 $this->userRepository = $userRepository; 20 $this->passwordHasher = $passwordHasher; 21 }
Hashing a Password During User Creation

Now that we have the UserPasswordHasherInterface injected into our UserService, we can proceed to modify the create method. This method will hash the plain text password provided during user registration before storing it in the database.

Here's how you can hash and store the password securely during user creation:

php
1public function create(string $username, string $plainPassword): User 2{ 3 $user = new User(); 4 $user->setUsername($username); 5 6 // Hash the plain password 7 $hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword); 8 $user->setPassword($hashedPassword); 9 10 $this->entityManager->persist($user); 11 $this->entityManager->flush(); 12 13 return $user; 14}

In this method, the hashPassword function of UserPasswordHasherInterface takes the plain text password and hashes it. We then set the hashed password to the User entity and persist it to the database. This ensures that passwords are always stored securely and never in plain text, significantly enhancing the security of our application.

Summary and Next Steps

In this lesson, we:

  • Highlighted the critical importance of password hashing for enhancing application security.
  • Implemented the PasswordAuthenticatedUserInterface in our User entity to standardize password handling.
  • Explored Symfony’s UserPasswordHasherInterface and its role in secure password storage.
  • Modified our UserService to hash passwords during user registration, ensuring they are never stored in plain text.

By following these steps, we've taken a significant stride towards securing user passwords in our Symfony MVC application. As you continue, apply this secured registration functionality in your practice exercises to reinforce your understanding and maintain robust security measures in your web applications.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.