Keiner der AJAX-Endpunkte hat einen Login geprueft. Ohne Anmeldung liessen
sich Kunden, Adressen und Rechnungen durchsuchen, und ajax/autosave.php
setzt $_POST ungeprueft in SQL (SQL-Injection ohne Login).
- incs/connectmysql.php und ajax/auto/connectmysql.php, die alle
AJAX-Endpunkte laden, antworten ohne gueltige Session mit 403
- stats.php verlangt eine Anmeldung
- db-backup.php und clean_db.php laufen nur noch per Kommandozeile;
ueber den Webserver konnte jeder einen Datenbank-Dump anstossen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der alte Code fuegt fast ueberall Zeilen ein, ohne alle NOT-NULL-Spalten zu
setzen; der nicht strikte Modus hat dann '', 0 oder ein Nulldatum
eingesetzt. Diese Werte stehen jetzt als DEFAULT im Schema. Weggelassene
Spalten verhalten sich damit wie bisher, ungueltige Werte, zu lange Texte
und falsche Verweise lehnt der strikte Modus weiterhin ab.
Das Migrationsskript meldet Spalten, die in der Quelle fehlen und ueber den
Standardwert gefuellt werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- sites/start.php: ein Query statt zwei, Passwortvergleich per hash_equals
in PHP statt im SQL, Updates per id. Die Eingaben gehen nicht mehr durch
htmlspecialchars(): Passwoerter werden beim Anlegen roh gehasht, mit
& < > " ' konnte man sich deshalb bisher nie anmelden.
- getUserSetting/updateUserSetting mit Platzhaltern; behoben: INSERT-Zweig
rief mysqli_real_escape_string ohne Verbindung auf (Fatal Error),
$mysqli_error als Variable, ungewolltes echo "update". Aufrufer escapen
nicht mehr vor (sonst doppelt), settings.php: Tippfehler
mysqi_real_escape_string beim Speichern von show_sks behoben, style nur
als Dateiname ohne Pfad.
- authServer.php, addKey.php, userkeys.php, checkAdduser(): Platzhalter.
Passkey-INSERT setzt lastAuth/authKey (Pflichtfelder im strikten Modus).
addKey nur als reine Ziffern, sonst passte "12345678xyz" per Typumwandlung.
- checkLogin() entfernt: nirgends aufgerufen, fragte die nicht existierende
Tabelle users ab.
- userkeys.tpl: Passkey-Name escaped.
Getestet gegen die migrierte Testdatenbank im strikten Modus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
isLocal() hielt jede Adresse aus 192.168.147.* fuer lokal. Das ist das
Docker-Netz auf dem NAS: Anfragen ueber den Docker- oder einen Reverse
Proxy tragen dessen Adresse und waeren damit als lokal durchgegangen
(Passwort-Login und Passkey-Freigabe von aussen). Der IPv6-Vergleich per
explode(":") scheiterte ausserdem an komprimierten Adressen.
- lokale Netze stehen als CIDR-Liste $lokale_netze in der externen Config
- ipImNetz() vergleicht IPv4/IPv6 korrekt per inet_pton, inkl. ::ffff:-Form
- Anfragen mit Proxy-Headern gelten nie als lokal
- ohne Konfiguration nur noch dasselbe IPv6-/64 wie der Server, kein IPv4
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
authServer.php nahm getCreateArgs/processCreate ohne jede Pruefung an und
uebernahm die Benutzer-id aus der URL. Jeder konnte damit aus dem Internet
einen Passkey fuer ein beliebiges Konto (auch Superadmin) registrieren und
sich anschliessend anmelden.
- sites/userkeys.php loest den addKey ein und legt eine Freigabe in der
Session ab (5 Minuten, ein Benutzer, einmalig); der addKey wird geloescht
- authServer.php verlangt fuer getCreateArgs/processCreate Login,
Superadmin-Recht und diese Freigabe; die Benutzer-id kommt aus der
Freigabe, nicht aus der URL
- queryFidoMetaDataService nur fuer Superadmins
- Passkey-Login verweigert gesperrte Benutzer und meldet keinen Erfolg
mehr, wenn der Benutzer fehlt
- addKey.php verlangt wie userkeys.php das Superadmin-Recht
- session_regenerate_id() nach Passwort- und Passkey-Login
- userkeys.php/addKey.php: $_GET["id"] als int statt ungequotet in SQL
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
update/migration-utf8mb4.php kopiert eine Instanz-Datenbank in eine neue
Datenbank mit update/schema.sql. Jeder Textwert wird einzeln eingeordnet
(korrekt, doppelt, mehrfach kodiert, unklar) und nur repariert, wenn die
Rueckfuehrung ueber MySQLs latin1-Abbildung eindeutig ist. Die Quelle bleibt
unveraendert.
- "pruefen" liest nur und gibt einen Bericht aus
- "migrieren <ziel>" legt das Ziel an, kopiert im strikten Modus, fuellt
fehlende Spalten und NULL in Pflichtfeldern mit Leerwerten, prueft die
Fremdschluessel und meldet verwaiste Verweise
Getestet gegen Dummy-Daten im alten Schema (778 doppelt, 28 mehrfach,
180 korrekt, 24 gemischt: alle Werte wie erwartet) und gegen das
abweichende ingwa-Schema.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Grundlage ist die Struktur der kunden-Datenbank. project und work kommen
aus ingwa, weil der Code sie fuer das Projekte-Modul braucht, sie in kunden
aber fehlen. Die Sicherungskopien altlasten_backup* sind nicht enthalten.
Tabellen, Spalten, Schluessel und Fremdschluessel sind sonst unveraendert,
nur Zeichensatz und Sortierung sind einheitlich utf8mb4_unicode_ci.
.gitignore laesst update/schema.sql als einzige .sql-Datei zu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- incs/db.php: db_verbinden() setzt utf8mb4, strikten sql_mode und schaltet
die mysqli-Exceptions von PHP 8.1+ ab, damit die bestehenden
"or error(mysqli_error())"-Pruefungen weiter greifen.
- db_abfrage/db_zeilen/db_zeile/db_wert mit ?-Platzhaltern
(mysqli_execute_query, PHP 8.2).
- Alle neun kopierten mysqli_connect/select_db-Stellen nutzen jetzt
db_verbinden().
Nur zusammen mit der auf utf8mb4 migrierten Datenbank einsetzbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SMTP-Passwoerter (mail_attachment), der MySQL-Zugang von stats.php und die
Schluessel fuer OpenWeatherMap und Aeris standen im Klartext im Code. Sie
kommen jetzt aus der externen Config ($smtp_konten, $stats_mysql_*,
$openweathermap_appid, $aeris_client_*). rebuild-settings.php legt die
Eintraege bei der Ersteinrichtung leer an.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Instanz:
- $instanz in der externen Config bestimmt INSTANZ, UPLOADS_PATH und
SESSION_NAME. Der Verzeichnisname waehlt nur noch die Config-Datei aus
und ist Fallback, solange $instanz fehlt.
- savePDF() schreibt nicht mehr fest nach DOCUMENT_ROOT."kunden", sondern
in das Verzeichnis, das es auch aufraeumt. .gitkeep wird nicht mehr
mitgeloescht.
Sicherheit:
- index.php?action=setup war ohne Login auch bei gueltiger Config
erreichbar. rebuild-settings.php schrieb $_POST ungeprueft als PHP-Code
in die Config (Codeausfuehrung, sofern die Datei schreibbar war).
- Setup nur noch ohne gueltige Config, firststart.php und
rebuild-settings.php pruefen das zusaetzlich selbst.
- Config-Werte werden per var_export() maskiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Entfernt, weil kein Code mehr darauf verwies:
- smarty3/ (Smarty 3.1.6; alle Aufrufer nutzen smarty/ mit 4.5.6)
- incs/tcpdf_old/
- incs/fpdf.php samt FPDF-Schriftmetriken in incs/font/*.php
und incs/font/makefont/ (nur von sites/makepdf_OLD.php genutzt,
parste ab PHP 8.0 ohnehin nicht mehr)
- sites/makepdf_OLD.php
- incs/users_old.php
- oldindex.php (nirgends verlinkter Alt-Einstiegspunkt)
Behalten: incs/PHPMailer_v5.1 (wird von sites/projectadd.php genutzt)
und die TTFs in incs/font (werden von stats.php fuer pChart geladen).
CLAUDE.md entsprechend nachgezogen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dokumentiert die Punkte, die sich nur aus dem Zusammenspiel mehrerer
Dateien erschliessen: Instanz-Ableitung aus dem Arbeitsverzeichnis,
externe Konfiguration, Request-Ablauf ueber checkRightsNInclude(),
das positionsbasierte Rechte-Schema, die bei jedem Request laufenden
Schema-Migrationen sowie tote Pfade und Stolperfallen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bestehende Codebasis als Ausgangsstand aufgenommen.
- .gitignore: Kundendaten (uploads_*), Smarty-Compile-Cache
(templates_c), Laufzeit-Temp und Datenbank-Dumps ausgeschlossen;
Verzeichnisstruktur ueber .gitkeep erhalten
- .gitattributes: Zeilenenden normalisiert (LF im Repo),
Binaerformate markiert
- db-backup.php: MySQL-Zugangsdaten kommen jetzt wie im Rest der
Anwendung aus der externen Konfiguration statt hartkodiert aus der
Datei; Passwort wird ueber eine temporaere Optionsdatei statt per
Kommandozeile uebergeben, Fehler von mysqldump/gzip werden gemeldet,
Aufraeumen aelterer Backups berechnet Jahr und Monat konsistent
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>