diff --git a/config/prod/fileload.php b/config/prod/fileload.php index ac5eada..f26a6cd 100644 --- a/config/prod/fileload.php +++ b/config/prod/fileload.php @@ -1,5 +1,26 @@ 0, // Session-Cookie + 'path' => '/', + 'domain' => '', // leer = aktuelle Domain + 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), + 'httponly' => true, + 'samesite' => 'Lax', // oder 'Strict' falls du sehr streng sein willst + ]); + + session_start(); + } +} require __DIR__ . "/config.php"; require __DIR__ . "/db.php"; require __DIR__ . '/../src/functions.php'; diff --git a/config/staging/fileload.php b/config/staging/fileload.php index be10a91..d6df5e1 100644 --- a/config/staging/fileload.php +++ b/config/staging/fileload.php @@ -2,6 +2,26 @@ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); +// Nur im Web-Kontext (nicht in CLI-Skripten) +if (php_sapi_name() !== 'cli') { + // Session nur starten, wenn noch keine läuft + if (session_status() === PHP_SESSION_NONE) { + + // Optional: Session-Konfiguration vor session_start + session_name('usbcheck_session'); + + session_set_cookie_params([ + 'lifetime' => 0, // Session-Cookie + 'path' => '/', + 'domain' => '', // leer = aktuelle Domain + 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), + 'httponly' => true, + 'samesite' => 'Lax', // oder 'Strict' falls du sehr streng sein willst + ]); + + session_start(); + } +} require __DIR__ . "/config.php"; require __DIR__ . "/db.php"; require __DIR__ . '/../src/functions.php'; diff --git a/partials/landing/login/login.php b/partials/landing/login/login.php index bfa5b4f..2083515 100644 --- a/partials/landing/login/login.php +++ b/partials/landing/login/login.php @@ -1,6 +1,19 @@
@@ -16,6 +29,20 @@ $authView = $authView ?? 'login';

+ +
+ +
+ +
+ + + + + + +
- \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess index a5f5e4a..374d47b 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -4,6 +4,14 @@ RewriteEngine On RewriteCond %{HTTP_HOST} (^|\.)ismyusbfake\.com$ [NC] RewriteRule ^$ /fakecheck/ [L,R=301] +# 1b) Auth-Routen auf Backend-Handler außerhalb von /public/ mappen +# /auth/login -> ../src/auth/login.php +# /auth/register -> ../src/auth/register.php +# /auth/logout -> ../src/auth/logout.php +RewriteRule ^auth/login/?$ ../src/auth/login.php [L] +RewriteRule ^auth/register/?$ ../src/auth/register.php [L] +RewriteRule ^auth/logout/?$ ../src/auth/logout.php [L] + # 2) Deine bisherigen Regeln RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d diff --git a/src/auth/login.php b/src/auth/login.php new file mode 100644 index 0000000..8b9ae54 --- /dev/null +++ b/src/auth/login.php @@ -0,0 +1,136 @@ +prepare('SELECT * FROM users WHERE email = :email LIMIT 1'); + $stmt->execute([':email' => $email]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); +} catch (Throwable $e) { + // DB-Fehler → generische Meldung + flash_set('error', 'Es ist ein Fehler beim Login aufgetreten. Bitte versuche es später erneut.', 'login'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); + exit; +} + +if (!$user) { + flash_set('error', 'E-Mail oder Passwort ist falsch.', 'login'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); + exit; +} + +// Account gesperrt? +if (!empty($user['is_locked'])) { + flash_set('error', 'Dein Konto ist gesperrt. Bitte kontaktiere den Support.', 'login'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); + exit; +} + +// Passwort prüfen +if (!password_verify($password, $user['password_hash'])) { + // Fehlversuche hochzählen (optional) + try { + $failed = (int)($user['failed_logins'] ?? 0) + 1; + + $lock = 0; + if ($failed >= 5) { + $lock = 1; + } + + $upd = $pdo->prepare('UPDATE users SET failed_logins = :failed, is_locked = :locked WHERE id = :id'); + $upd->execute([ + ':failed' => $failed, + ':locked' => $lock, + ':id' => $user['id'], + ]); + } catch (Throwable $e) { + // Ignorieren, Login-Fehler reicht + } + + $msg = 'E-Mail oder Passwort ist falsch.'; + if (($user['failed_logins'] ?? 0) + 1 >= 5) { + $msg = 'Zu viele Fehlversuche. Dein Konto wurde vorübergehend gesperrt.'; + } + + flash_set('error', $msg, 'login'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); + exit; +} + +// Passwort korrekt → Fehlversuche zurücksetzen & letztes Login speichern +try { + $upd = $pdo->prepare('UPDATE users SET failed_logins = 0, last_login_at = NOW() WHERE id = :id'); + $upd->execute([':id' => $user['id']]); +} catch (Throwable $e) { + // Nicht kritisch für den Nutzer +} + +// Session füllen +if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); +} + +// Initialen bauen +$firstName = $user['first_name'] ?? ''; +$lastName = $user['last_name'] ?? ''; +$initials = ''; + +if ($firstName !== '') { + $initials .= mb_substr($firstName, 0, 1); +} +if ($lastName !== '') { + $initials .= mb_substr($lastName, 0, 1); +} +if ($initials === '') { + $initials = mb_substr($user['username'] ?? $user['email'], 0, 2); +} +$initials = mb_strtoupper($initials); + +$_SESSION['user'] = [ + 'id' => $user['id'], + 'email' => $user['email'], + 'username' => $user['username'], + 'first_name' => $user['first_name'], + 'last_name' => $user['last_name'], + 'plan' => $user['plan'] ?? 'free', + 'initials' => $initials, +]; + +// Flash für „Willkommen“ +flash_set('success', 'Willkommen zurück, ' . ($user['first_name'] ?: 'User') . '!', 'login'); + +// Redirect-Ziel prüfen (nur interne Pfade erlauben) +$target = is_string($redirect) ? trim($redirect) : '/'; +if ($target === '' || $target[0] !== '/') { + $target = '/'; +} + +// Sprache ggf. anfügen, wenn noch nicht als Parameter vorhanden +$sep = (strpos($target, '?') === false) ? '?' : '&'; +$target = $target . $sep . 'lang=' . urlencode($lang); + +header('Location: ' . $target); +exit; diff --git a/src/auth/logout.php b/src/auth/logout.php new file mode 100644 index 0000000..0d1064b --- /dev/null +++ b/src/auth/logout.php @@ -0,0 +1,22 @@ +prepare('SELECT id FROM users WHERE email = :email LIMIT 1'); + $stmt->execute([':email' => $email]); + $existing = $stmt->fetch(PDO::FETCH_ASSOC); +} catch (Throwable $e) { + flash_set('error', 'Es ist ein Fehler bei der Registrierung aufgetreten. Bitte versuche es später erneut.', 'register'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth'); + exit; +} + +if ($existing) { + flash_set('error', 'Diese E-Mail-Adresse ist bereits registriert.', 'register'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth'); + exit; +} + +// Username aus E-Mail ableiten (oder einfach die komplette E-Mail nutzen) +$username = $email; + +// Vor- und Nachname grob aus dem „Name“-Feld splitten +$firstName = $name; +$lastName = null; + +$parts = preg_split('/\s+/', $name); +if (count($parts) >= 2) { + $firstName = array_shift($parts); + $lastName = implode(' ', $parts); +} + +$passwordHash = password_hash($password, PASSWORD_DEFAULT); + +try { + $stmt = $pdo->prepare(' + INSERT INTO users + (username, email, password_hash, first_name, last_name, plan) + VALUES + (:username, :email, :password_hash, :first_name, :last_name, :plan) + '); + $stmt->execute([ + ':username' => $username, + ':email' => $email, + ':password_hash' => $passwordHash, + ':first_name' => $firstName, + ':last_name' => $lastName, + ':plan' => 'free', + ]); + + $userId = (int)$pdo->lastInsertId(); +} catch (Throwable $e) { + flash_set('error', 'Die Registrierung ist fehlgeschlagen. Bitte versuche es später erneut.', 'register'); + header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth'); + exit; +} + +// Direkt einloggen +if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); +} + +$initials = ''; +if ($firstName !== '') { + $initials .= mb_substr($firstName, 0, 1); +} +if ($lastName !== null && $lastName !== '') { + $initials .= mb_substr($lastName, 0, 1); +} +if ($initials === '') { + $initials = mb_substr($username, 0, 2); +} +$initials = mb_strtoupper($initials); + +$_SESSION['user'] = [ + 'id' => $userId, + 'email' => $email, + 'username' => $username, + 'first_name' => $firstName, + 'last_name' => $lastName, + 'plan' => 'free', + 'initials' => $initials, +]; + +flash_set('success', 'Konto erfolgreich erstellt. Willkommen bei USBCheck!', 'login'); + +// Redirect-Ziel prüfen (nur interne Pfade) +$target = is_string($redirect) ? trim($redirect) : '/'; +if ($target === '' || $target[0] !== '/') { + $target = '/'; +} + +$sep = (strpos($target, '?') === false) ? '?' : '&'; +$target = $target . $sep . 'lang=' . urlencode($lang); + +header('Location: ' . $target); +exit; diff --git a/src/functions.php b/src/functions.php index 611e21c..a370445 100644 --- a/src/functions.php +++ b/src/functions.php @@ -76,39 +76,78 @@ function tpl_add_style(string $href, string $pos = 'header', ?string $version = function tpl(string $file, string $type = 'structure', string $site = 'main'): void { - - // Basisordner $base = __DIR__ . '/../partials/'; - // Erlaubte Typen & Sites - $allowedTypes = ['structure', 'landing']; - $allowedSites = ['main', 'fakecheck','login']; - - // Validierung - if (!in_array($type, $allowedTypes)) { - $type = 'structure'; + // VALIDIERUNG: Nur einfache Check, kein Path-Traversal + if (preg_match('/[^a-zA-Z0-9_\-]/', $file)) { + echo ""; + return; + } + if (preg_match('/[^a-zA-Z0-9_\-]/', $type)) { + echo ""; + return; + } + if (preg_match('/[^a-zA-Z0-9_\-]/', $site)) { + echo ""; + return; } - if (!in_array($site, $allowedSites)) { - $site = 'main'; - } - - // Zielpfad konstruieren if ($type === 'landing') { - // landing -> landing/{site}/{file}.php $path = $base . "landing/$site/$file.php"; } else { - // structure -> structure/{file}.php $path = $base . "structure/$file.php"; } - // 🔹 alle globalen Variablen (aus index.php) in den lokalen Scope holen + extract($GLOBALS, EXTR_SKIP); - // Datei laden if (file_exists($path)) { include $path; } else { echo ""; } - +} + + +/** + * Flash-Meldung setzen (wird genau einmal nach Redirect angezeigt). + * + * @param string $type z.B. 'success', 'error', 'info', 'warning' + * @param string $message Die Meldung für den Nutzer + */ +function flash_set(string $type, string $message, string $context = null): void +{ + if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); + } + + $_SESSION['flash'] = [ + 'type' => $type, + 'message' => $message, + 'context' => $context, + ]; +} + +/** + * Flash-Meldung holen und direkt löschen (Einmal-Anzeige). + * + * @return array|null ['type' => 'success|error|info|warning', 'message' => '...'] + */ +function flash_get(): ?array +{ + if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); + } + + if (empty($_SESSION['flash']) || !is_array($_SESSION['flash'])) { + return null; + } + + $flash = $_SESSION['flash']; + unset($_SESSION['flash']); + + $flash['type'] = $flash['type'] ?? 'info'; + $flash['message'] = $flash['message'] ?? ''; + $flash['context'] = $flash['context'] ?? null; + + return $flash; }