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-bundlephp bin/console make:reset-passwordGeneriert 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
- Nutzer gibt seine E-Mail-Adresse auf
/reset-passwordein. - Symfony erzeugt einen zeitlich begrenzten, EINMALIGEN Reset-Token und speichert einen HASH davon (nicht den Token selbst!) in der Datenbank.
- Eine E-Mail mit einem Link (inkl. Token) wird verschickt (Kapitel 38 vertieft den E-Mail-Versand).
- Klickt der Nutzer den Link, prüft Symfony den Token gegen den gespeicherten Hash.
- 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
reset_password:
request_password_repository: App\Repository\ResetPasswordRequestRepository
lifetime: 3600 # 1 Stunde in Sekunden
throttle_limit: 3600 # verhindert wiederholte Anfragen innerhalb dieser Zeitthrottle_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.