Security: OAuth-state, Verschlüsselung, Session-Timeout & weitere Härtung
- m365_callback.php: OAuth-state-Validierung gegen Login-CSRF - includes/Crypto.php: Verschlüsselung-at-rest für UniFi-Passwörter (AES-256-GCM/libsodium) mit Klartext-Fallback für Bestandsinstallationen - install.php: APP_KEY-Generierung + Reinstall nur mit Admin-Session - Auth.php: absolutes Session-Timeout (SESSION_LIFETIME) durchsetzen - index.php: CSRF + Throttle auch für anonyme öffentliche Voucher-Erstellung - UniFiController.php: createVoucher liefert nicht mehr den falschen Code bei parallelen Erstellungen (note-Match statt blindes reset()) - display_errors in allen Entry-Points deaktiviert, log_errors aktiviert - test.php & m365_debug.php hinter requireAdmin() (Info-Leak) - m365_debug.php: abgeschnittene/kaputte Datei vervollständigt
This commit is contained in:
parent
bf3e55a967
commit
3483da274f
17 changed files with 306 additions and 36 deletions
|
|
@ -1,4 +1,6 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/Crypto.php';
|
||||
|
||||
class UniFiController {
|
||||
private $controllerUrl;
|
||||
private $username;
|
||||
|
|
@ -169,16 +171,44 @@ class UniFiController {
|
|||
throw new Exception("Voucher konnte nicht erstellt werden");
|
||||
}
|
||||
|
||||
// Voucher-Code abrufen
|
||||
// Voucher-Code abrufen. WICHTIG: getVouchers() liefert die Voucher
|
||||
// unsortiert zurueck – ein blindes reset() kann bei parallelen
|
||||
// Erstellungen den falschen (fremden) Code liefern. Daher gezielt
|
||||
// nach dem soeben erstellten Voucher suchen: gleiche note + neueste
|
||||
// create_time.
|
||||
$vouchers = $this->getVouchers();
|
||||
|
||||
|
||||
if (empty($vouchers)) {
|
||||
throw new Exception("Voucher-Code konnte nicht abgerufen werden");
|
||||
}
|
||||
|
||||
// Neuesten Voucher zurückgeben
|
||||
$latestVoucher = reset($vouchers);
|
||||
|
||||
|
||||
$latestVoucher = null;
|
||||
foreach ($vouchers as $voucher) {
|
||||
// Nur Voucher mit passender Notiz beruecksichtigen
|
||||
if (($voucher['note'] ?? null) !== $voucherName) {
|
||||
continue;
|
||||
}
|
||||
if ($latestVoucher === null
|
||||
|| ($voucher['create_time'] ?? 0) > ($latestVoucher['create_time'] ?? 0)) {
|
||||
$latestVoucher = $voucher;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: falls keine note-Uebereinstimmung (z.B. Sonderzeichen),
|
||||
// den global neuesten Voucher nehmen.
|
||||
if ($latestVoucher === null) {
|
||||
foreach ($vouchers as $voucher) {
|
||||
if ($latestVoucher === null
|
||||
|| ($voucher['create_time'] ?? 0) > ($latestVoucher['create_time'] ?? 0)) {
|
||||
$latestVoucher = $voucher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($latestVoucher === null || empty($latestVoucher['code'])) {
|
||||
throw new Exception("Voucher-Code konnte nicht abgerufen werden");
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => $latestVoucher['code'],
|
||||
'formatted_code' => $this->formatVoucherCode($latestVoucher['code']),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue