add setup

This commit is contained in:
Jonas Hinterdorfer 2025-11-17 08:16:25 +00:00
commit 48179e1f82
4 changed files with 790 additions and 0 deletions

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM php:8.2-apache
# Install PDO MySQL driver
RUN docker-php-ext-install pdo pdo_mysql mysqli
# Enable Apache rewrite (optional, but often useful)
RUN a2enmod rewrite

19
docker-compose.yaml Normal file
View File

@ -0,0 +1,19 @@
services:
web:
build: .
ports:
- "80:80"
volumes:
- ./src:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: testdb
ports:
- "3306:3306"
volumes:
- ./database1:/var/lib/mysql

36
setup.sql Normal file
View File

@ -0,0 +1,36 @@
-- Datenbank erstellen (falls nicht durch Docker-Umgebung bereits erstellt)
CREATE DATABASE IF NOT EXISTS testdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE testdb;
-- Bank-Tabelle erstellen
CREATE TABLE IF NOT EXISTS bank (
iban VARCHAR(34) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
passwd VARCHAR(255) NOT NULL,
balance DECIMAL(10, 2) DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Transaktions-Tabelle erstellen
CREATE TABLE IF NOT EXISTS transf (
sender VARCHAR(34) NOT NULL,
recipient VARCHAR(34) NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
time INT UNSIGNED NOT NULL,
text VARCHAR(500) DEFAULT '',
PRIMARY KEY (sender, recipient, time),
INDEX idx_sender (sender, time),
INDEX idx_recipient (recipient, time),
INDEX idx_time (time),
FOREIGN KEY (sender) REFERENCES bank(iban) ON DELETE CASCADE,
FOREIGN KEY (recipient) REFERENCES bank(iban) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Test-Accounts erstellen (Passwort: "test123" für alle)
INSERT INTO bank (iban, name, passwd, balance) VALUES
('DE1234', 'Max Mustermann', '$2y$10$YourHashedPasswordHere1', 5000.00),
('DE5678', 'Lisa Schmidt', '$2y$10$YourHashedPasswordHere2', 3500.00),
('DE9012', 'Thomas Müller', '$2y$10$YourHashedPasswordHere3', 12000.00)
ON DUPLICATE KEY UPDATE balance=balance;

728
src/index.php Normal file
View File

@ -0,0 +1,728 @@
<?php
/**
* NSCS-Bank - Sichere Version mit Registrierung
* Alle Sicherheitslücken behoben + Registrierungs-Feature
*/
// Sicherheits-Konfiguration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 0); // Auf 1 setzen wenn HTTPS verfügbar
ini_set('session.use_strict_mode', 1);
ini_set('display_errors', 0); // In Produktion: keine Fehler anzeigen
error_reporting(E_ALL);
session_start();
// Datenbank-Verbindung
function getDB() {
static $db = null;
if ($db === null) {
try {
$db = new PDO("mysql:host=db;dbname=testdb;charset=utf8mb4", "root", "rootpass", [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]);
} catch (PDOException $e) {
die("Datenbankverbindung fehlgeschlagen db: " . $e->getMessage());
}
}
return $db;
}
// Sicheres Passwort-Hashing
function secureHash($password) {
return password_hash($password, PASSWORD_ARGON2ID);
}
function verifyPassword($password, $hash) {
return password_verify($password, $hash);
}
// CSRF-Token-Management
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function validateCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
// Sichere Ausgabe
function e($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
// Input-Validierung
function validateIBAN($iban) {
return preg_match('/^[A-Z0-9]{6,34}$/', $iban);
}
function validateAmount($amount) {
return is_numeric($amount) && $amount >= 1 && $amount <= 1000;
}
function validateName($name) {
return strlen($name) >= 3 && strlen($name) <= 100 && preg_match('/^[a-zA-ZäöüÄÖÜß\s\-]+$/u', $name);
}
// Session-Prüfung
function requireLogin() {
if (!isset($_SESSION['iban'])) {
header('Location: index.php');
exit;
}
}
// === REGISTRIERUNG ===
if (isset($_GET['act']) && $_GET['act'] == "register") {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST["name"] ?? "");
$pass = $_POST["pass"] ?? "";
$pass_confirm = $_POST["pass_confirm"] ?? "";
// Validierung
$errors = [];
if (!validateName($name)) {
$errors[] = "Name muss 3-100 Zeichen lang sein und darf nur Buchstaben, Leerzeichen und Bindestriche enthalten";
}
if (strlen($pass) < 8) {
$errors[] = "Passwort muss mindestens 8 Zeichen haben";
}
if ($pass !== $pass_confirm) {
$errors[] = "Passwörter stimmen nicht überein";
}
if (empty($errors)) {
$db = getDB();
// Generiere eindeutige IBAN
do {
$iban = 'DE' . str_pad(rand(0, 999999999999), 12, '0', STR_PAD_LEFT);
$stmt = $db->prepare("SELECT COUNT(*) FROM bank WHERE iban = ?");
$stmt->execute([$iban]);
} while ($stmt->fetchColumn() > 0);
// Benutzer anlegen
try {
$hash = secureHash($pass);
$stmt = $db->prepare("INSERT INTO bank (iban, name, passwd, balance) VALUES (?, ?, ?, ?)");
$stmt->execute([$iban, $name, $hash, 1000.00]); // Startguthaben: 1000€
$_SESSION['msg'] = "Registrierung erfolgreich! Ihre IBAN: $iban - Startguthaben: 1000€";
$_SESSION['reg_iban'] = $iban; // Für Anzeige auf Login-Seite
header("Location: index.php");
exit;
} catch (PDOException $e) {
$_SESSION['emsg'] = "Registrierung fehlgeschlagen";
header("Location: index.php?act=register");
exit;
}
} else {
$_SESSION['emsg'] = implode("<br>", $errors);
header("Location: index.php?act=register");
exit;
}
}
}
// === Überweisung durchführen ===
if (isset($_GET['recv']) && isset($_SESSION['iban'])) {
requireLogin();
// CSRF-Token prüfen (aus POST)
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
$_SESSION['emsg'] = "Sicherheitsfehler - ungültiges Token";
header('Location: index.php?act=main');
exit;
}
$recv = trim($_GET['recv']);
$amount = $_GET['amount'];
$text = trim($_GET['text'] ?? "");
// Input-Validierung
if (!validateIBAN($recv)) {
$_SESSION['emsg'] = "Ungültige Empfänger-IBAN";
header('Location: index.php?act=main');
exit;
}
if (!validateAmount($amount)) {
$_SESSION['emsg'] = "Ungültiger Betrag (1-1000)";
header('Location: index.php?act=main');
exit;
}
if (strlen($text) > 200) {
$_SESSION['emsg'] = "Text zu lang (max. 200 Zeichen)";
header('Location: index.php?act=main');
exit;
}
$db = getDB();
// Kontostand prüfen
$stmt = $db->prepare("SELECT balance FROM bank WHERE iban = ?");
$stmt->execute([$_SESSION["iban"]]);
$r = $stmt->fetch();
if (!$r) {
header('Location: index.php');
exit;
}
$bal = $r['balance'];
if ($bal + 1000 < $amount) {
$_SESSION['emsg'] = "Kontostand zu niedrig";
header('Location: index.php?act=main');
exit;
}
// Empfängerkonto prüfen
$stmt = $db->prepare("SELECT balance FROM bank WHERE iban = ?");
$stmt->execute([$recv]);
if (!$stmt->fetch()) {
$_SESSION['emsg'] = "Empfängerkonto existiert nicht";
header('Location: index.php?act=main');
exit;
}
// Transaktion durchführen
try {
$db->beginTransaction();
$stmt = $db->prepare("UPDATE bank SET balance = balance - ? WHERE iban = ?");
$stmt->execute([$amount, $_SESSION["iban"]]);
$stmt = $db->prepare("UPDATE bank SET balance = balance + ? WHERE iban = ?");
$stmt->execute([$amount, $recv]);
$stmt = $db->prepare("INSERT INTO transf (sender, recipient, amount, time, text) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$_SESSION["iban"], $recv, $amount, time(), $text]);
$db->commit();
$_SESSION['msg'] = "Überweisung erfolgreich durchgeführt";
} catch (Exception $e) {
$db->rollBack();
$_SESSION['emsg'] = "Überweisung fehlgeschlagen";
}
header('Location: index.php?act=main');
exit;
}
$act = $_GET['act'] ?? "";
// === Passwort ändern ===
if ($act == "chpass") {
requireLogin();
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
$_SESSION['emsg'] = "Sicherheitsfehler";
header('Location: index.php?act=main');
exit;
}
$pass = $_POST["pass"] ?? "";
if (strlen($pass) < 8) {
$_SESSION['emsg'] = "Passwort muss mindestens 8 Zeichen haben";
header('Location: index.php?act=main');
exit;
}
$hash = secureHash($pass);
$db = getDB();
$stmt = $db->prepare("UPDATE bank SET passwd = ? WHERE iban = ?");
$stmt->execute([$hash, $_SESSION["iban"]]);
$_SESSION['msg'] = "Passwort wurde erfolgreich geändert";
header('Location: index.php?act=main');
exit;
}
// === Name suchen ===
if ($act == "search") {
requireLogin();
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
$_SESSION['emsg'] = "Sicherheitsfehler";
header('Location: index.php?act=main');
exit;
}
$name = trim($_POST["name"] ?? "");
if (strlen($name) > 100) {
$_SESSION['emsg'] = "Suchbegriff zu lang";
header('Location: index.php?act=main');
exit;
}
$db = getDB();
// SICHER: Prepared Statement mit LIKE
$stmt = $db->prepare("SELECT iban, name FROM bank WHERE name LIKE ? LIMIT 50");
$stmt->execute(["%$name%"]);
$a = [];
while ($r = $stmt->fetch()) {
array_push($a, [$r['iban'], $r['name']]);
}
$_SESSION['iban_names'] = $a;
header('Location: index.php?act=main');
exit;
}
// === Login ===
if ($act == "login") {
$iban = trim($_POST["iban"] ?? "");
$pass = $_POST["pass"] ?? "";
if (!validateIBAN($iban)) {
$_SESSION['emsg'] = "Ungültige IBAN";
header("Location: index.php");
exit;
}
$db = getDB();
$stmt = $db->prepare("SELECT iban, passwd, name FROM bank WHERE iban = ?");
$stmt->execute([$iban]);
$r = $stmt->fetch();
if ($r && verifyPassword($pass, $r['passwd'])) {
// Login erfolgreich
session_regenerate_id(true); // Session-Fixation verhindern
$_SESSION['iban'] = $r['iban'];
$_SESSION['name'] = $r['name'];
header("Location: index.php?act=main");
} else {
$_SESSION['emsg'] = "Login fehlgeschlagen - IBAN oder Passwort falsch";
header("Location: index.php");
}
exit;
}
// === Logout ===
if ($act == "logout") {
session_destroy();
header("Location: index.php");
exit;
}
// === Transfer-Details anzeigen ===
if ($act == "showtrans") {
requireLogin();
$id = $_GET["id"] ?? 0;
if (!is_numeric($id)) {
header('Location: index.php?act=main');
exit;
}
$db = getDB();
$stmt = $db->prepare("SELECT sender, recipient, amount, time, text FROM transf WHERE time = ?");
$stmt->execute([$id]);
$r = $stmt->fetch();
if (!$r) {
$_SESSION['emsg'] = "Transaktion nicht gefunden";
header('Location: index.php?act=main');
exit;
}
// Prüfen ob User berechtigt ist diese Transaktion zu sehen
if ($r['sender'] !== $_SESSION['iban'] && $r['recipient'] !== $_SESSION['iban']) {
$_SESSION['emsg'] = "Keine Berechtigung";
header('Location: index.php?act=main');
exit;
}
$src = $r['sender'];
$dest = $r['recipient'];
$amount = $r['amount'];
$text = $r['text'];
$stmt = $db->prepare("SELECT name FROM bank WHERE iban = ?");
$stmt->execute([$src]);
$srcn = $stmt->fetch()['name'] ?? "Unbekannt";
$stmt->execute([$dest]);
$destn = $stmt->fetch()['name'] ?? "Unbekannt";
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NSCS-Bank - Sicher</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 500px;
margin: 0 auto;
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
}
.container-wide {
max-width: 900px;
}
h1, h2, h3 { color: #667eea; margin-bottom: 20px; }
h1 { font-size: 28px; text-align: center; }
.alert {
padding: 14px 18px;
border-radius: 8px;
margin-bottom: 20px;
font-weight: 500;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-danger { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.alert-info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 8px; font-weight: 600; font-size: 14px; color: #555; }
input[type="text"], input[type="password"], input[type="number"] {
width: 100%;
padding: 12px 14px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
transition: border-color 0.2s;
}
input:focus { outline: none; border-color: #667eea; }
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
width: 100%;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-secondary { background: #6c757d; color: white; }
.btn-secondary:hover { background: #5a6268; }
.btn-outline {
background: white;
color: #667eea;
border: 2px solid #667eea;
}
.btn-outline:hover {
background: #667eea;
color: white;
}
.info-box {
background: linear-gradient(135deg, #e0e7ff 0%, #f3e7ff 100%);
padding: 20px;
border-radius: 10px;
margin: 20px 0;
border-left: 5px solid #667eea;
}
.balance {
font-size: 42px;
font-weight: bold;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 20px 0;
text-align: center;
}
.transaction-item {
padding: 14px;
border: 2px solid #f0f0f0;
border-radius: 8px;
margin-bottom: 12px;
font-family: monospace;
font-size: 13px;
transition: border-color 0.2s;
}
.transaction-item:hover {
border-color: #667eea;
}
.search-result {
padding: 10px;
border-bottom: 1px solid #eee;
font-family: monospace;
font-size: 14px;
}
hr { border: none; border-top: 2px solid #f0f0f0; margin: 30px 0; }
table { border-collapse: collapse; width: 100%; }
td { padding: 8px; }
.link-text {
text-align: center;
margin-top: 20px;
font-size: 14px;
}
.link-text a {
color: #667eea;
text-decoration: none;
font-weight: 600;
}
.link-text a:hover {
text-decoration: underline;
}
.feature-list {
margin-top: 10px;
margin-left: 20px;
color: #555;
}
.feature-list li {
margin-bottom: 6px;
}
</style>
</head>
<body>
<div class="container <?php echo ($act == 'main') ? 'container-wide' : ''; ?>">
<?php
// Nachrichten anzeigen
if (isset($_SESSION['msg'])) {
echo '<div class="alert alert-success">' . e($_SESSION['msg']) . '</div>';
unset($_SESSION['msg']);
}
if (isset($_SESSION['emsg'])) {
echo '<div class="alert alert-danger">' . e($_SESSION['emsg']) . '</div>';
unset($_SESSION['emsg']);
}
// === REGISTRIERUNGS-SEITE ===
if ($act == "register") {
?>
<h1>🏦 Neues Konto eröffnen</h1>
<div class="info-box">
<strong>📝 Registrierung:</strong>
<ul class="feature-list">
<li>Automatische IBAN-Generierung</li>
<li>Startguthaben: 1.000</li>
<li>Sichere Passwort-Verschlüsselung</li>
</ul>
</div>
<form method="post" action="index.php?act=register">
<div class="form-group">
<label>Vollständiger Name:</label>
<input name="name" type="text" required maxlength="100" placeholder="z.B. Max Mustermann">
</div>
<div class="form-group">
<label>Passwort (min. 8 Zeichen):</label>
<input name="pass" type="password" required minlength="8">
</div>
<div class="form-group">
<label>Passwort wiederholen:</label>
<input name="pass_confirm" type="password" required minlength="8">
</div>
<button type="submit" class="btn btn-primary">Konto eröffnen</button>
</form>
<div class="link-text">
Bereits ein Konto? <a href="index.php">Zum Login</a>
</div>
<?php
}
// === LOGIN-SEITE ===
else if ($act == "" || !isset($_SESSION['iban'])) {
?>
<h1>🏦 NSCS-Bank</h1>
<?php if (isset($_SESSION['reg_iban'])): ?>
<div class="alert alert-info">
<strong> Ihre neue IBAN:</strong><br>
<span style="font-family: monospace; font-size: 18px; font-weight: bold;">
<?= e($_SESSION['reg_iban']) ?>
</span><br>
<small>Bitte notieren Sie sich diese IBAN für den Login!</small>
</div>
<?php unset($_SESSION['reg_iban']); endif; ?>
<div class="info-box">
<strong> Sicherheitsfeatures:</strong>
<ul class="feature-list">
<li>Argon2 Passwort-Hashing</li>
<li>SQL-Injection-Schutz</li>
<li>XSS-Prävention</li>
<li>CSRF-Token-Validierung</li>
<li>Session-Sicherheit</li>
</ul>
</div>
<form method="post" action="index.php?act=login">
<div class="form-group">
<label>IBAN:</label>
<input name="iban" type="text" required maxlength="34" placeholder="z.B. DE89370400440532013000">
</div>
<div class="form-group">
<label>Passwort:</label>
<input name="pass" type="password" required>
</div>
<button type="submit" class="btn btn-primary">Anmelden</button>
</form>
<div class="link-text">
Noch kein Konto? <a href="index.php?act=register">Jetzt registrieren</a>
</div>
<?php
}
// === TRANSFER-DETAIL-SEITE ===
else if ($act == "showtrans") {
?>
<h1>Transfer Detail</h1>
<div class="info-box">
<p><strong>Sender:</strong> <?= e($src) ?> - <?= e($srcn) ?></p>
<p><strong>Empfänger:</strong> <?= e($dest) ?> - <?= e($destn) ?></p>
<p><strong>Betrag:</strong> <?= e($amount) ?> €</p>
<p><strong>Zeit:</strong> <?= e(date('Y-m-d H:i', $id)) ?></p>
<p><strong>Text:</strong> <?= e($text) ?></p>
</div>
<form action="index.php">
<input type="hidden" name="act" value="main">
<button type="submit" class="btn btn-secondary">Zurück</button>
</form>
<?php
}
// === HAUPTSEITE ===
else if ($act == "main") {
$name = $_SESSION['name'];
$db = getDB();
$stmt = $db->prepare("SELECT balance FROM bank WHERE iban = ?");
$stmt->execute([$_SESSION["iban"]]);
$r = $stmt->fetch();
$bal = $r['balance'];
?>
<h1>Hallo <i><?= e($name) ?></i>! 👋</h1>
<hr>
<div class="info-box">
<p><strong>IBAN:</strong> <span style="font-family: monospace;"><?= e($_SESSION["iban"]) ?></span></p>
<p class="balance"><?= e(number_format($bal, 2, ',', '.')) ?> €</p>
</div>
<hr>
<h2>📋 Überweisungsliste</h2>
<?php
date_default_timezone_set('Europe/Vienna');
$stmt = $db->prepare("SELECT sender, recipient, amount, time FROM transf WHERE (sender = ? OR recipient = ?) AND time > ? ORDER BY time DESC");
$stmt->execute([$_SESSION["iban"], $_SESSION["iban"], time() - 10 * 3600]);
$has_transactions = false;
while ($r = $stmt->fetch()) {
$has_transactions = true;
echo '<div class="transaction-item">';
echo e(number_format($r['amount'], 2, ',', '.')) . '€ ' . e($r['sender']) . ' ➔ ' . e($r['recipient']) . ' - ';
echo '<a href="index.php?act=showtrans&id=' . e($r['time']) . '" style="color: #667eea;">Detail</a>';
echo '</div>';
}
if (!$has_transactions) {
echo '<p style="text-align: center; color: #999; padding: 20px;">Keine Transaktionen in den letzten 10 Stunden</p>';
}
?>
<hr>
<h2>💸 Neue Überweisung</h2>
<form action="index.php" method="post" onsubmit="this.action='index.php?recv='+this.recv.value+'&amount='+this.amount.value+'&text='+encodeURIComponent(this.text.value); return confirm('Überweisung wirklich durchführen?');">
<input type="hidden" name="csrf_token" value="<?= e(generateCSRFToken()) ?>">
<div class="form-group">
<label>Empfänger IBAN:</label>
<input name="recv" type="text" required maxlength="34">
</div>
<div class="form-group">
<label>Betrag ():</label>
<input name="amount" type="number" min="1" max="1000" step="0.01" required>
</div>
<div class="form-group">
<label>Verwendungszweck:</label>
<input name="text" type="text" maxlength="200">
</div>
<button type="submit" class="btn btn-primary">Durchführen</button>
</form>
<hr>
<h2>🔍 IBAN suchen</h2>
<form method="post" action="index.php?act=search">
<input type="hidden" name="csrf_token" value="<?= e(generateCSRFToken()) ?>">
<div class="form-group">
<label>Name:</label>
<input name="name" type="text" required maxlength="100">
</div>
<button type="submit" class="btn btn-secondary">Suchen</button>
</form>
<?php
if (isset($_SESSION['iban_names'])) {
echo '<div style="margin-top: 15px; background: #f9f9f9; padding: 15px; border-radius: 8px;">';
echo '<h3>Suchergebnisse:</h3>';
$a = $_SESSION['iban_names'];
if (count($a) == 0) {
echo '<p style="color: #999;">Keine Ergebnisse gefunden</p>';
} else {
foreach ($a as $item) {
echo '<div class="search-result">' . e($item[0]) . ' - ' . e($item[1]) . '</div>';
}
}
echo '</div>';
unset($_SESSION['iban_names']);
}
?>
<hr>
<h2>🔒 Passwort ändern</h2>
<form method="post" action="index.php?act=chpass">
<input type="hidden" name="csrf_token" value="<?= e(generateCSRFToken()) ?>">
<div class="form-group">
<label>Neues Passwort (min. 8 Zeichen):</label>
<input name="pass" type="password" required minlength="8">
</div>
<button type="submit" class="btn btn-secondary">Ändern</button>
</form>
<hr>
<form action="index.php?act=logout" method="post">
<button type="submit" class="btn btn-outline">Abmelden</button>
</form>
<?php
}
?>
</div>
</body>
</html>