Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Passwort zurücksetzen

Passwort zurücksetzen

~14 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Zum Abschluss von Block 5 lösen wir ein Problem, das JEDE Anwendung mit Login früher oder später braucht: "Passwort vergessen?" – sicher umgesetzt, ohne dass sich jemand fremde Konten übernehmen kann.

Das ResetPasswordBundle installieren

composer require symfonycasts/reset-password-bundle
php bin/console make:reset-password

Generiert eine Entity ResetPasswordRequest, ein Repository, einen Controller UND die nötigen Templates – ein größerer, mehrteiliger Workflow, den wir hier Schritt für Schritt nachvollziehen.

Der Workflow im Überblick

  1. Nutzer gibt seine E-Mail-Adresse auf /reset-password ein.
  2. Symfony erzeugt einen zeitlich begrenzten, EINMALIGEN Reset-Token und speichert einen HASH davon (nicht den Token selbst!) in der Datenbank.
  3. Eine E-Mail mit einem Link (inkl. Token) wird verschickt (Kapitel 38 vertieft den E-Mail-Versand).
  4. Klickt der Nutzer den Link, prüft Symfony den Token gegen den gespeicherten Hash.
  5. Ist der Token gültig UND nicht abgelaufen, darf ein neues Passwort gesetzt werden.

Warum ein Hash des Tokens gespeichert wird, nicht der Token selbst

Achtung: GENAU dasselbe Prinzip wie bei Passwörtern (Kapitel 28): Würde der ROHE Reset-Token in der Datenbank liegen, könnte JEDER mit Datenbank-Zugriff (Leck, böswilliger Insider) damit SELBST ein fremdes Passwort zurücksetzen. Der GEHASHTE Token in der Datenbank ist dagegen wertlos, ohne den Original-Token (der nur per E-Mail verschickt wird) zu kennen.

Schritt 1: Die Reset-Anfrage

use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;

#[Route('/reset-password', name: 'app_forgot_password_request')]
public function request(
    Request $request,
    ResetPasswordHelperInterface $resetPasswordHelper,
    UserRepository $userRepository,
): Response {
    if ($request->isMethod('POST')) {
        $email = $request->request->get('email');
        $user = $userRepository->findOneBy(['email' => $email]);

        if ($user !== null) {
            $resetToken = $resetPasswordHelper->generateResetToken($user);
            // E-Mail mit $resetToken->getToken() versenden (Kapitel 38)
        }

        // IMMER dieselbe Meldung zeigen, unabhängig davon, ob die E-Mail existiert!
        $this->addFlash('info', 'Falls ein Konto mit dieser E-Mail existiert, wurde eine Nachricht versendet.');

        return $this->redirectToRoute('app_login');
    }

    return $this->render('reset_password/request.html.twig');
}

Achtung: Die IDENTISCHE Erfolgsmeldung, egal ob die E-Mail existiert oder nicht, ist ABSICHTLICH: Eine unterschiedliche Meldung ("E-Mail nicht gefunden" vs. "Nachricht versendet") würde einem Angreifer verraten, WELCHE E-Mail-Adressen als registrierte Konten existieren – ein sogenanntes "User Enumeration"-Leck.

Schritt 2: Das Passwort tatsächlich zurücksetzen

#[Route('/reset-password/reset/{token}', name: 'app_reset_password')]
public function reset(
    string $token,
    Request $request,
    ResetPasswordHelperInterface $resetPasswordHelper,
    UserPasswordHasherInterface $passwordHasher,
    EntityManagerInterface $entityManager,
): Response {
    try {
        $user = $resetPasswordHelper->validateTokenAndFetchUser($token);
    } catch (\Exception) {
        $this->addFlash('error', 'Der Link ist ungültig oder abgelaufen.');
        return $this->redirectToRoute('app_forgot_password_request');
    }

    if ($request->isMethod('POST')) {
        $neuesPasswort = $request->request->get('password');

        $user->setPassword($passwordHasher->hashPassword($user, $neuesPasswort));
        $resetPasswordHelper->removeResetRequest($token);
        $entityManager->flush();

        $this->addFlash('success', 'Ihr Passwort wurde erfolgreich geändert.');
        return $this->redirectToRoute('app_login');
    }

    return $this->render('reset_password/reset.html.twig');
}

validateTokenAndFetchUser() prüft AUTOMATISCH: existiert ein passender Hash? Ist er noch nicht abgelaufen (Standard: 1 Stunde)? removeResetRequest() macht den Token nach erfolgreicher Nutzung UNGÜLTIG – ein Reset-Link funktioniert GENAU EINMAL, nicht mehrfach.

Ablaufzeit konfigurieren

config/packages/reset_password.yaml
reset_password:
    request_password_repository: App\Repository\ResetPasswordRequestRepository
    lifetime: 3600  # 1 Stunde in Sekunden
    throttle_limit: 3600  # verhindert wiederholte Anfragen innerhalb dieser Zeit

throttle_limit ist ein zusätzlicher Schutz gegen Missbrauch: verhindert, dass für DIESELBE E-Mail-Adresse in kurzer Folge unzählige Reset-E-Mails ausgelöst werden (was sowohl ein Spam-Vektor als auch ein Ressourcen-Erschöpfungs-Risiko wäre).

Damit ist Block 5 (Security & Authentifizierung) abgeschlossen! Unser Aufgaben-Manager hat jetzt vollständige Nutzerverwaltung: Registrierung, Login, objektbezogene Zugriffskontrolle, API-Authentifizierung und Passwort-Reset. Block 6 widmet sich Symfonys eigentlichem Herzstück – Services, Dependency Injection und Events.