db->query('SELECT COUNT(*) FROM konto')->fetchColumn() > 0; } public function anlegen(string $name, string $passwort): void { $this->db->prepare('INSERT INTO konto (name, passwort, erstellt) VALUES (?, ?, ?)') ->execute([$name, password_hash($passwort, PASSWORD_DEFAULT), Datenbank::jetzt()]); } public function gesperrt(string $ip): bool { $abfrage = $this->db->prepare('SELECT COUNT(*) FROM anmeldeversuch WHERE ip = ? AND zeit > ?'); $abfrage->execute([$ip, gmdate('Y-m-d\TH:i:s\Z', time() - self::SPERRE_SEKUNDEN)]); return (int) $abfrage->fetchColumn() >= self::VERSUCHE; } /** @return array{id: int, name: string}|null */ public function pruefen(string $name, string $passwort, string $ip): ?array { $abfrage = $this->db->prepare('SELECT id, name, passwort FROM konto WHERE name = ?'); $abfrage->execute([$name]); $konto = $abfrage->fetch() ?: null; // Auch ohne Konto einmal hashen: Die Antwortzeit verrät sonst, welche Namen es gibt. $hash = null !== $konto ? (string) $konto['passwort'] : password_hash('attrappe', PASSWORD_DEFAULT); if (!password_verify($passwort, $hash) || null === $konto) { $this->fehlversuch($ip); return null; } if (password_needs_rehash($hash, PASSWORD_DEFAULT)) { $this->db->prepare('UPDATE konto SET passwort = ? WHERE id = ?')->execute([password_hash($passwort, PASSWORD_DEFAULT), $konto['id']]); } $this->db->prepare('DELETE FROM anmeldeversuch WHERE ip = ?')->execute([$ip]); return ['id' => (int) $konto['id'], 'name' => (string) $konto['name']]; } public function fehlversuch(string $ip): void { $this->db->prepare('INSERT INTO anmeldeversuch (ip, zeit) VALUES (?, ?)')->execute([$ip, Datenbank::jetzt()]); $this->db->prepare('DELETE FROM anmeldeversuch WHERE zeit < ?')->execute([gmdate('Y-m-d\TH:i:s\Z', time() - 86400)]); } }