Implementing Password Recovery on a Website: Secure Flow

Problem: Typical Mistakes in Implementing Password Recovery

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1281
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    977
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1026
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1103
  • image_website-_0.webp
    Website development for Red Pear
    550

Problem: Typical Mistakes in Implementing Password Recovery

The standard Forgot Password flow seems simple: the user enters an email, receives a link, sets a new password. But in practice, developers make mistakes that lead to vulnerabilities: revealing registration status, token guessing, rate limit bypass. We configure the flow on Laravel using the built-in Password Broker: the token is generated, hashed with bcrypt, and stored in the password_resets table with a TTL of 60 minutes. The response to a recovery request is identical for both existing and non-existing emails — an attacker cannot tell if a user is registered. OWASP recommends bcrypt for token hashing, and we follow this practice.

How the Recovery Flow Works?

The user sends a POST request to /forgot-password with their email. The server creates a token, hashes it, and stores it in the DB. Then an email is sent with a link like https://example.com/reset-password?token=...&email=.... The user clicks the link, sees a form for a new password. After submitting POST /reset-password, the server verifies the token (compares the bcrypt hash), updates the password, and removes the token. All active user sessions are invalidated.

What Vulnerabilities Does the Standard Implementation Hide?

Storing the Token in Plaintext

If the token is stored in plaintext, a database breach allows an attacker to immediately reset any user's password. We use a bcrypt hash — even a compromised database does not reveal tokens. bcrypt is specifically designed for password hashing: it is slow and includes a salt. MD5 and SHA-1/2 allow millions of combinations per second, while bcrypt slows brute force by a factor of 1000, making an attack impractical.

Lack of Rate Limiting

Without rate limiting, an attacker can flood the server with reset requests, causing load on the mail server. We set a limit: no more than 3 requests per hour per email or IP.

Identical Response for Existing and Non-Existing Emails

Many return different errors ("email not found" vs "email sent"), allowing an attacker to enumerate the email base. Our solution always responds identically: "If the email is registered, the email has been sent."

Why bcrypt Instead of MD5/SHA?

bcrypt is specifically designed for password hashing: it is slow and includes a salt. MD5 and SHA-1/2 are fast hashes that allow an attacker to try millions of combinations per second. Bcrypt slows brute force by a factor of 1000 or more, making an attack impractical. This means our bcrypt approach is 1000 times safer than storing the token in plaintext.

How We Implement Password Recovery Turnkey

We use the Laravel Password Broker, which provides ready-made methods for generating, verifying, and deleting tokens. Example controller:

use Illuminate\Support\Facades\Password; class ForgotPasswordController extends Controller { public function __invoke(Request $request) { $request->validate(['email' => 'required|email']); $status = Password::sendResetLink($request->only('email')); return $status === Password::RESET_LINK_SENT ? back()->with(['status' => __($status)]) : back()->withErrors(['email' => __($status)]); } } 

For password reset we use Password::reset().

Token storage: password_resets table with fields email, token (bcrypt), created_at. The token lives 60 minutes, then is removed via cron or garbage collection. Example table:

email token (bcrypt) created_at
[email protected] $2y$10$...
[email protected] $2y$10$...

Thanks to bcrypt, even if the DB is leaked, tokens cannot be decrypted — this is 1000 times safer than plaintext.

Typical Implementation Mistakes

  • Storing the token in plaintext
  • Lack of rate limiting
  • Different responses for existing/non-existing emails
  • No session invalidation after password change
  • Too long token TTL (more than 1 hour)

Comparison of Approaches for Reliability

Approach Security Complexity
bcrypt-hashed token (ours) High Low
Plaintext token Low Very low
JWT token with short TTL Medium Medium

Our approach is the optimal balance of security and maintainability. JWT with short TTL requires additional infrastructure and does not protect against token leakage if the secret key is compromised. Certified engineers guarantee implementation quality.

Work Process

  1. Analysis of the current authentication system and risk identification.
  2. Flow design: endpoints, validation, rate limiting.
  3. Implementation of controllers, migrations, custom email template.
  4. Testing: unit tests for reset and recovery, integration tests for rate limiting.
  5. Deployment and monitoring.

Timeline and What's Included

Typically implementation takes 1–2 business days. Includes:

  • Custom controller and routes
  • Migration for the password_resets table
  • Custom email template (HTML+text)
  • Rate limiting (3 requests/hour per email)
  • Session invalidation upon password change
  • Tests and documentation

We have many years of experience and have completed over 30 authentication projects. Get a consultation — we will assess your project for free. Order a turnkey secure password recovery implementation — contact us.