220124a54a
[FÁZE-1][auth] Přidána proměnná $auth_logout_html s připraveným odhlašovacím formulářem
261 lines
9.0 KiB
PHP
261 lines
9.0 KiB
PHP
<?php
|
||
// ============================================================
|
||
// HLAVNÍ SOUBOR AUTENTIZACE
|
||
// ============================================================
|
||
// Tento soubor vložíš na začátek každé stránky, kterou chceš
|
||
// chránit přihlášením:
|
||
//
|
||
// require_once 'auth/auth.php';
|
||
//
|
||
// Po jeho načtení máš k dispozici tyto proměnné:
|
||
//
|
||
// $auth_prihlasen ... true / false (vždy nastaveno)
|
||
// $auth_uzivatel['id'] ... ID přihlášeného uživatele (nebo null)
|
||
// $auth_uzivatel['email'] ... email přihlášeného uživatele (nebo null)
|
||
// $auth_uzivatel['admin'] ... true / false (vždy nastaveno)
|
||
// $auth_logout_html ... HTML formulář s tlačítkem odhlášení,
|
||
// nebo prázdný řetězec pokud není přihlášen
|
||
//
|
||
// Použití odhlašovacího tlačítka na chráněné stránce:
|
||
//
|
||
// echo $auth_logout_html;
|
||
//
|
||
// ============================================================
|
||
|
||
require_once __DIR__ . '/config.php';
|
||
require_once __DIR__ . '/db.php';
|
||
|
||
// ------------------------------------------------------------
|
||
// SPUŠTĚNÍ SESSION
|
||
// ------------------------------------------------------------
|
||
|
||
// Nastavíme parametry session cookie ještě PŘED session_start().
|
||
// HttpOnly = JavaScript nemůže cookie číst (ochrana před XSS)
|
||
// SameSite = cookie se neposílá při požadavcích z jiných webů
|
||
// (ochrana před CSRF)
|
||
// Secure = cookie se posílá jen přes HTTPS (pokud web běží na HTTPS)
|
||
session_name(SESSION_NAZEV);
|
||
session_set_cookie_params([
|
||
'lifetime' => 0, // 0 = cookie platí do zavření prohlížeče
|
||
// (trvalost zajišťuje remember me, ne session)
|
||
'path' => '/',
|
||
'httponly' => true, // JavaScript k cookie nemá přístup
|
||
'samesite' => 'Strict', // ochrana před CSRF
|
||
// 'secure' => true, // odkomentuj pokud web běží na HTTPS
|
||
]);
|
||
session_start();
|
||
|
||
// ------------------------------------------------------------
|
||
// VÝCHOZÍ STAV – uživatel není přihlášen
|
||
// ------------------------------------------------------------
|
||
// Tyto hodnoty jsou nastaveny vždy – při každém načtení stránky.
|
||
// Teprve níže se případně přepíší na true / skutečné hodnoty.
|
||
|
||
$auth_prihlasen = false;
|
||
$auth_uzivatel = [
|
||
'id' => null,
|
||
'email' => null,
|
||
'admin' => false,
|
||
];
|
||
$auth_logout_html = '';
|
||
|
||
// ------------------------------------------------------------
|
||
// KROK 1: Existuje platná session?
|
||
// ------------------------------------------------------------
|
||
// Session je nejrychlejší způsob ověření – nevyžaduje dotaz do DB.
|
||
// Session data jsou uložena na serveru, uživatel je nemůže zfalšovat.
|
||
|
||
if (isset($_SESSION['uzivatel_id']) && isset($_SESSION['email'])) {
|
||
|
||
// Kontrola expirace session (nečinnost)
|
||
if (isset($_SESSION['posledni_aktivita']) &&
|
||
(time() - $_SESSION['posledni_aktivita']) > SESSION_EXPIRACE) {
|
||
|
||
// Session vypršela – zrušíme ji
|
||
$_SESSION = [];
|
||
session_destroy();
|
||
|
||
} else {
|
||
|
||
// Session je platná – uživatel je přihlášen
|
||
$auth_prihlasen = true;
|
||
$auth_uzivatel = [
|
||
'id' => $_SESSION['uzivatel_id'],
|
||
'email' => $_SESSION['email'],
|
||
// Přetypování na bool – ochrana pro případ, že by v session
|
||
// byla jiná hodnota než true/false
|
||
'admin' => (bool) $_SESSION['admin'],
|
||
];
|
||
|
||
// Aktualizujeme čas poslední aktivity
|
||
$_SESSION['posledni_aktivita'] = time();
|
||
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// KROK 2: Session neexistuje – zkusíme remember me cookie
|
||
// ------------------------------------------------------------
|
||
// Cookie obsahuje pouze selector a token. Nikdy neobsahuje
|
||
// heslo, ID uživatele ani příznak admin. Vše citlivé je na serveru.
|
||
|
||
if (!$auth_prihlasen &&
|
||
isset($_COOKIE['auth_selector']) &&
|
||
isset($_COOKIE['auth_token'])) {
|
||
|
||
$cookie_selector = $_COOKIE['auth_selector'];
|
||
$cookie_token = $_COOKIE['auth_token'];
|
||
|
||
// Vyhledáme záznam v DB podle selectoru (přesná shoda, rychlé)
|
||
$stmt = $pdo->prepare("
|
||
SELECT
|
||
t.id AS token_id,
|
||
t.uzivatel_id,
|
||
t.token_hash,
|
||
t.expiruje,
|
||
u.email,
|
||
u.admin
|
||
FROM `" . DB_TABULKA_TOKENY . "` t
|
||
JOIN `" . DB_TABULKA_UZIVATELE . "` u ON u.id = t.uzivatel_id
|
||
WHERE t.selector = :selector
|
||
LIMIT 1
|
||
");
|
||
$stmt->execute([':selector' => $cookie_selector]);
|
||
$zaznam = $stmt->fetch();
|
||
|
||
$cookie_ok = false;
|
||
|
||
if ($zaznam) {
|
||
|
||
// Zkontrolujeme, zda token ještě nevypršel
|
||
if (time() < strtotime($zaznam['expiruje'])) {
|
||
|
||
// Ověříme tajný token pomocí password_verify().
|
||
// Tato funkce záměrně trvá stejně dlouho bez ohledu
|
||
// na výsledek – chrání před timing útoky.
|
||
if (password_verify($cookie_token, $zaznam['token_hash'])) {
|
||
$cookie_ok = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($cookie_ok) {
|
||
|
||
// Cookie je platná – obnovíme session
|
||
// Regenerujeme session ID (ochrana před session fixation)
|
||
session_regenerate_id(true);
|
||
|
||
$_SESSION['uzivatel_id'] = $zaznam['uzivatel_id'];
|
||
$_SESSION['email'] = $zaznam['email'];
|
||
$_SESSION['admin'] = (bool) $zaznam['admin'];
|
||
$_SESSION['posledni_aktivita'] = time();
|
||
|
||
// Vygenerujeme CSRF token pokud ještě neexistuje
|
||
if (empty($_SESSION['csrf_token'])) {
|
||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||
}
|
||
|
||
$auth_prihlasen = true;
|
||
$auth_uzivatel = [
|
||
'id' => $zaznam['uzivatel_id'],
|
||
'email' => $zaznam['email'],
|
||
'admin' => (bool) $zaznam['admin'],
|
||
];
|
||
|
||
// Prodloužíme platnost tokenu v DB i v cookie
|
||
$nova_expirace = time() + REMEMBER_EXPIRACE;
|
||
$nova_expirace_dt = date('Y-m-d H:i:s', $nova_expirace);
|
||
|
||
$stmt2 = $pdo->prepare("
|
||
UPDATE `" . DB_TABULKA_TOKENY . "`
|
||
SET `expiruje` = :expiruje
|
||
WHERE `id` = :id
|
||
");
|
||
$stmt2->execute([
|
||
':expiruje' => $nova_expirace_dt,
|
||
':id' => $zaznam['token_id'],
|
||
]);
|
||
|
||
// Prodloužíme cookie v prohlížeči
|
||
setcookie('auth_selector', $cookie_selector, [
|
||
'expires' => $nova_expirace,
|
||
'path' => '/',
|
||
'httponly' => true,
|
||
'samesite' => 'Strict',
|
||
// 'secure' => true,
|
||
]);
|
||
setcookie('auth_token', $cookie_token, [
|
||
'expires' => $nova_expirace,
|
||
'path' => '/',
|
||
'httponly' => true,
|
||
'samesite' => 'Strict',
|
||
// 'secure' => true,
|
||
]);
|
||
|
||
// Smažeme staré (expirované) tokeny z DB příležitostně
|
||
$stmt3 = $pdo->prepare("
|
||
DELETE FROM `" . DB_TABULKA_TOKENY . "`
|
||
WHERE `expiruje` < :ted
|
||
");
|
||
$stmt3->execute([':ted' => date('Y-m-d H:i:s')]);
|
||
|
||
} else {
|
||
|
||
// Cookie je neplatná nebo expirovaná – smažeme ji
|
||
if ($zaznam) {
|
||
$stmt4 = $pdo->prepare("
|
||
DELETE FROM `" . DB_TABULKA_TOKENY . "`
|
||
WHERE `id` = :id
|
||
");
|
||
$stmt4->execute([':id' => $zaznam['token_id']]);
|
||
}
|
||
|
||
setcookie('auth_selector', '', [
|
||
'expires' => time() - 3600,
|
||
'path' => '/',
|
||
'httponly' => true,
|
||
'samesite' => 'Strict',
|
||
]);
|
||
setcookie('auth_token', '', [
|
||
'expires' => time() - 3600,
|
||
'path' => '/',
|
||
'httponly' => true,
|
||
'samesite' => 'Strict',
|
||
]);
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// KROK 3: Rozhodnutí – přesměrovat nebo pokračovat?
|
||
// ------------------------------------------------------------
|
||
|
||
if (!$auth_prihlasen && VYZADOVAT_PRIHLASENI) {
|
||
|
||
// Uložíme URL aktuální stránky pro přesměrování po přihlášení
|
||
$aktualni_url = $_SERVER['REQUEST_URI'] ?? '';
|
||
if (!empty($aktualni_url)) {
|
||
$_SESSION['redirect_po_prihlaseni'] = $aktualni_url;
|
||
}
|
||
|
||
header('Location: ' . AUTH_LOGIN_URL);
|
||
exit;
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// PŘIPRAVENÝ HTML KÓD PRO ODHLAŠOVACÍ TLAČÍTKO
|
||
// ------------------------------------------------------------
|
||
// Na chráněné stránce stačí napsat:
|
||
//
|
||
// echo $auth_logout_html;
|
||
//
|
||
// Pokud uživatel není přihlášen, proměnná je prázdný řetězec
|
||
// a nevypíše se nic.
|
||
|
||
if ($auth_prihlasen) {
|
||
$auth_logout_html =
|
||
'<form method="POST" action="' . htmlspecialchars(AUTH_LOGOUT_URL) . '">'
|
||
. '<input type="hidden" name="csrf_token" value="'
|
||
. htmlspecialchars($_SESSION['csrf_token'] ?? '') . '">'
|
||
. '<button type="submit">Odhlásit se</button>'
|
||
. '</form>';
|
||
} |