Resetowanie hasła przez e-mail (tokeny)
- Użytkownicy i logowanie
- poziom: zaawansowany
- aktualizacja: 12 lipca 2026
Reset hasła wydaje się prosty, ale łatwo o groźne błędy — np. zapisanie tokenu w bazie „jawnie" albo zdradzanie, czy dany e-mail ma konto. Ten skrypt robi to porządnie: token generuje kryptograficznie, do bazy trafia wyłącznie jego hasz SHA-256, link wygasa po godzinie i jest jednorazowy.
Całość zamyka się w jednym pliku obsługującym oba kroki: prośbę o link oraz ustawienie nowego hasła.
PHP
152 linii
Pobierz .php
<?php
/**
* Resetowanie hasła przez e-mail — skryptyPHP.com
* Zapisz jako: reset-hasla.php
* Wymaga tabeli `uzytkownicy` (skrypt „Rejestracja użytkowników") oraz:
*
* CREATE TABLE resety_hasla (
* id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
* uzytkownik_id INT UNSIGNED NOT NULL,
* token_hash CHAR(64) NOT NULL,
* wazny_do DATETIME NOT NULL,
* INDEX (token_hash),
* FOREIGN KEY (uzytkownik_id) REFERENCES uzytkownicy(id) ON DELETE CASCADE
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/
session_start();
require 'baza.php'; // skrypt „Połączenie z bazą (PDO)"
$pdo = polacz_z_baza();
// ── USTAWIENIA ──────────────────────────────────────────────
$adres_strony = 'https://twojadomena.pl'; // bez ukośnika na końcu
$adres_nadawcy = 'powiadomienia@twojadomena.pl';
$token = $_GET['token'] ?? '';
$komunikat = '';
$blad = '';
// ═══ KROK 2: użytkownik kliknął link i ustawia nowe hasło ═══
if ($token !== '') {
// Szukamy WAŻNEGO tokenu po jego haszu
$stmt = $pdo->prepare(
'SELECT r.id, r.uzytkownik_id
FROM resety_hasla r
WHERE r.token_hash = ? AND r.wazny_do > NOW()'
);
$stmt->execute([hash('sha256', $token)]);
$reset = $stmt->fetch();
if (!$reset) {
$blad = 'Link jest nieprawidłowy lub wygasł. Poproś o nowy.';
$token = '';
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$haslo = $_POST['haslo'] ?? '';
$haslo2 = $_POST['haslo2'] ?? '';
if (mb_strlen($haslo) < 8) {
$blad = 'Hasło musi mieć co najmniej 8 znaków.';
} elseif ($haslo !== $haslo2) {
$blad = 'Hasła nie są identyczne.';
} else {
// Zapis nowego hasła…
$pdo->prepare('UPDATE uzytkownicy SET haslo_hash = ? WHERE id = ?')
->execute([
password_hash($haslo, PASSWORD_DEFAULT),
$reset['uzytkownik_id'],
]);
// …i unieważnienie WSZYSTKICH tokenów tego użytkownika
$pdo->prepare('DELETE FROM resety_hasla WHERE uzytkownik_id = ?')
->execute([$reset['uzytkownik_id']]);
$komunikat = 'Hasło zmienione! Możesz się zalogować.';
$token = '';
}
}
}
// ═══ KROK 1: użytkownik podaje e-mail, wysyłamy link ═══
if ($token === '' && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['email'])) {
$email = mb_strtolower(trim($_POST['email']));
$stmt = $pdo->prepare('SELECT id FROM uzytkownicy WHERE email = ?');
$stmt->execute([$email]);
$uzytkownik = $stmt->fetch();
if ($uzytkownik) {
// 32 losowe bajty -> 64 znaki hex; w bazie tylko hasz tokenu
$nowyToken = bin2hex(random_bytes(32));
$pdo->prepare(
'INSERT INTO resety_hasla (uzytkownik_id, token_hash, wazny_do)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 1 HOUR))'
)->execute([$uzytkownik['id'], hash('sha256', $nowyToken)]);
$link = $adres_strony . '/reset-hasla.php?token=' . $nowyToken;
$tresc = "Cześć!\n\nAby ustawić nowe hasło, kliknij poniższy link "
. "(ważny przez 1 godzinę):\n\n$link\n\n"
. "Jeśli to nie Ty prosiłeś o zmianę hasła, zignoruj tę wiadomość.";
$naglowki = 'From: ' . $adres_nadawcy . "\r\n"
. 'Content-Type: text/plain; charset=UTF-8' . "\r\n";
mail($email, '=?UTF-8?B?' . base64_encode('Reset hasła') . '?=',
$tresc, $naglowki);
}
// Ten sam komunikat niezależnie od tego, czy konto istnieje!
$komunikat = 'Jeśli podany adres istnieje w naszej bazie, '
. 'wysłaliśmy na niego link do zmiany hasła.';
}
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Reset hasła</title>
<style>
.formularz { max-width: 420px; font-family: sans-serif; margin: 20px auto; }
.formularz label { display: block; margin: 12px 0 4px; font-weight: bold; }
.formularz input { width: 100%; padding: 8px; box-sizing: border-box; }
.formularz button { margin-top: 16px; padding: 10px 24px; cursor: pointer; }
.info { color: #1b7a2f; }
.blad { color: #b00020; }
</style>
</head>
<body>
<div class="formularz">
<h2>Reset hasła</h2>
<?php if ($komunikat): ?><p class="info"><?= htmlspecialchars($komunikat) ?></p><?php endif; ?>
<?php if ($blad): ?><p class="blad"><?= htmlspecialchars($blad) ?></p><?php endif; ?>
<?php if ($token !== ''): ?>
<!-- KROK 2: formularz nowego hasła -->
<form method="post" action="?token=<?= htmlspecialchars(urlencode($token)) ?>">
<label for="haslo">Nowe hasło (min. 8 znaków)</label>
<input type="password" id="haslo" name="haslo" required minlength="8"
autocomplete="new-password">
<label for="haslo2">Powtórz nowe hasło</label>
<input type="password" id="haslo2" name="haslo2" required minlength="8"
autocomplete="new-password">
<button type="submit">Ustaw nowe hasło</button>
</form>
<?php else: ?>
<!-- KROK 1: formularz z adresem e-mail -->
<form method="post" action="reset-hasla.php">
<label for="email">Adres e-mail Twojego konta</label>
<input type="email" id="email" name="email" required>
<button type="submit">Wyślij link do resetu</button>
</form>
<?php endif; ?>
</div>
</body>
</html>
Jak użyć tego skryptu
- Utwórz tabelę
resety_haslazapytaniem z komentarza (wymaga istniejącej tabeliuzytkownicy). - W ustawieniach wpisz adres swojej strony i adres nadawcy w swojej domenie, po czym zapisz plik jako
reset-hasla.php. - Podlinkuj stronę z formularza logowania:
<a href="reset-hasla.php">Nie pamiętam hasła</a>. - Przetestuj cały obieg: prośba → e-mail → link → nowe hasło → logowanie.
Warto wiedzieć
- W bazie trzymamy wyłącznie hasz SHA-256 tokenu. Gdyby ktoś wykradł zawartość tabeli, i tak nie zbuduje z niej działających linków.
- Komunikat po podaniu e-maila jest zawsze taki sam — napastnik nie sprawdzi w ten sposób, kto ma u Ciebie konto.
- Po udanej zmianie hasła usuwamy wszystkie tokeny użytkownika — każdy link jest jednorazowy.
- Warto dopisać sprzątanie wygasłych rekordów, np. raz dziennie:
DELETE FROM resety_hasla WHERE wazny_do < NOW().
#reset hasła
#tokeny
#random_bytes
#mail
#bezpieczeństwo
Podobne skrypty
Użytkownicy i logowanie
Rejestracja użytkowników z password_hash()
Bezpieczna rejestracja kont: walidacja danych, sprawdzenie unikalności e-maila, haszowanie hasła funkcją password_hash() i zapis do bazy przez PDO.
średni Zobacz kod → Użytkownicy i logowanieLogowanie użytkowników i ochrona podstron
Logowanie na sesjach PHP: weryfikacja hasła przez password_verify(), odnowienie identyfikatora sesji, wylogowanie oraz funkcja wymagaj_logowania() do ochrony podstron.
średni Zobacz kod →