diff --git a/docs/m0/security-baseline.md b/docs/m0/security-baseline.md index 938f4f8..59a996e 100644 --- a/docs/m0/security-baseline.md +++ b/docs/m0/security-baseline.md @@ -11,7 +11,7 @@ Testing, markiert aber die wichtigsten Risiken fuer die SaaS-Umstrukturierung. | --- | --- | --- | --- | | Hart codierte DB-Zugangsdaten | `jahresauswertung.php` Zeilen 4-8 | Secret-Leak, direkte Produktiv-DB-Gefahr | Zugangsdaten rotieren, Skript deaktivieren oder auf Env-Konfiguration umstellen | | Schreibende Seiten ohne eigene Rollenpruefung | `stricheintragen.php`, `einzahlung.php`, `mailversenden.php`, `exportKaffeeliste.php` | Direkter URL-Aufruf kann Aktionen erlauben | Jede Seite serverseitig mit `requireRole` absichern | -| Keine CSRF-Token | nahezu alle POST-/Delete-Formulare | Ungewollte Buchungen, Loeschungen, Imports | CSRF fuer alle schreibenden Aktionen | +| CSRF-Schutz nur teilweise vorhanden | viele POST-/Delete-Formulare; M2 hat `hinweise.php`, `mitarbeiterverwalten.php`, `namenanpassen.php` abgesichert | Ungewollte Buchungen, Loeschungen, Imports | CSRF fuer alle verbleibenden schreibenden Aktionen | | Harte Deletes fuer Buchungen | `letzteneintraege.php` | Audit-Historie und Revisionsfaehigkeit gehen verloren | Storno-/Reversal-Modell statt Delete | | Upload in Webroot | `csvupload.php` Zeilen 131-138 | Dateiablage kann missbraucht werden | Upload ausserhalb Webroot, Dateityp/MIME/Name pruefen | | Kein Tenant-Scope | alle fachlichen Queries | Zentrales SaaS-Leak-Risiko | `tenant_id` verpflichtend und Query-Schicht testen | @@ -52,7 +52,7 @@ Fuer SaaS gilt: 1. Secrets rotieren und aus dem Code entfernen. 2. Legacy-Schreibseiten bis zum Umbau hinter explizite Admin-Pruefung setzen. -3. CSRF-Schutz in der neuen App-Basis einplanen. +3. CSRF-Schutz schrittweise auf alle verbleibenden Legacy-Schreibseiten ausrollen. 4. Finanzdaten im Zielmodell nur noch stornieren, nicht loeschen. 5. Uploads ausserhalb des Webroots modellieren. 6. Einheitliche Escape-/View-Helfer einfuehren. diff --git a/docs/m2-technical-foundation.md b/docs/m2-technical-foundation.md index e7110c5..3089e63 100644 --- a/docs/m2-technical-foundation.md +++ b/docs/m2-technical-foundation.md @@ -35,6 +35,23 @@ Legacy-Verhalten oder das bestehende Design zu veraendern. werden spaeter einzeln umgestellt, damit keine Formulare oder Spezialflows brechen. +### CSRF-Rollout + +Erste Legacy-POST-Seiten sind opt-in abgesichert: + +- `hinweise.php`: Hinweis anlegen und Hinweis loeschen. Der bisherige + GET-Loeschlink wurde durch ein POST-Formular mit CSRF-Token ersetzt. +- `mitarbeiterverwalten.php`: Mitglied anlegen, Bearbeitungsformular oeffnen, + Mitglied speichern, aktivieren und deaktivieren. +- `namenanpassen.php`: Anzeigenamen aktualisieren. + +Noch offen: + +- Buchungsflows: `index.php`, `stricheintragen.php`, `einzahlung.php`. +- Korrektur-/Loeschflows: `letzteneintraege.php`. +- Uploads: `csvupload.php`. +- Spezialprozesse: `mailversenden.php`, `jahresauswertung.php`. + ### Migrationen - `database/migrations/0001_legacy_mysql_baseline.sql` bildet die bisherige @@ -82,21 +99,28 @@ Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`, - PHP-Syntax fuer Bootstrap, Migrationen, Init-Skript und Config ist sauber. - Session-Start laeuft in der lokalen Dev-Umgebung ohne PHP-Warnings ueber `var/sessions`. +- CSRF negative Tests: `hinweise.php`, `mitarbeiterverwalten.php` und + `namenanpassen.php` liefern bei POST ohne Token HTTP 419. +- CSRF positive Tests: gueltige Token funktionieren fuer Hinweis-Anlage, + Mitglieder-Bearbeitungsformular und Namensanpassung. Der temporaere + Testhinweis wurde wieder entfernt. - Golden Master weiterhin gruen mit 104 Assertions. - HTTP-Smoke weiterhin gruen mit 14 sicheren Seiten. ## Bewusste Grenzen - Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehoeren zu M3. -- Keine globale CSRF-Erzwingung in M2. Die Absicherung der POST-Seiten erfolgt - schrittweise. +- Keine globale CSRF-Erzwingung in M2. Die Absicherung weiterer POST-Seiten + erfolgt schrittweise. - Kein Layout-Umbau in M2. Die visuelle Struktur bleibt stabil. - PDF-/Mail-/Jahresprozesse bleiben als M6-Themen offen. ## Naechste Schritte -1. POST-Seiten einzeln mit `app_csrf_field()` und `app_require_csrf()` - absichern. -2. Eine duenne View-/Layout-Struktur vorbereiten, ohne Header/Footer-Markup +1. Buchungs- und Korrektur-POST-Seiten einzeln mit `app_csrf_field()` und + `app_require_csrf()` absichern. +2. CSV-Upload gesondert absichern und spaeter Uploads ausserhalb des Webroots + verlegen. +3. Eine duenne View-/Layout-Struktur vorbereiten, ohne Header/Footer-Markup sofort zu verschieben. -3. Danach M3 starten: Tenants, User, Registrierung, Login und Rollen. +4. Danach M3 starten: Tenants, User, Registrierung, Login und Rollen. diff --git a/docs/saas-umstrukturierungsplan.md b/docs/saas-umstrukturierungsplan.md index 825ac24..a352d2a 100644 --- a/docs/saas-umstrukturierungsplan.md +++ b/docs/saas-umstrukturierungsplan.md @@ -381,7 +381,9 @@ Schritte: bleiben vorerst Legacy-Wrapper. - Bestehende Assets weiterverwenden. - CSRF- und Session-Basis einziehen. Session und CSRF-Helper sind vorhanden; - globale Erzwingung erfolgt schrittweise pro POST-Seite. + globale Erzwingung erfolgt schrittweise pro POST-Seite. Erste Seiten sind + abgesichert: `hinweise.php`, `mitarbeiterverwalten.php`, + `namenanpassen.php`. - Konfigurationswerte aus Code in Umgebung oder Settings verschieben. Ergebnis: diff --git a/hinweise.php b/hinweise.php index 7944bdb..64b578a 100644 --- a/hinweise.php +++ b/hinweise.php @@ -1,6 +1,7 @@ Kaffeeliste - Hinweise"; - // Hinweis löschen - if (isset($_GET['delete'])) { - $id = (int)$_GET['delete']; - $stmt = sqlsrv_query($conn, "DELETE FROM kl_hinweise WHERE id = ?", [$id]); - } - - // Hinweis speichern + // Hinweis speichern oder löschen if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $nachricht = $_POST['nachricht']; - - - $gueltig_bis = $_POST['gueltig_bis']; // z.B. "2025-09-03T14:00" - $dt = DateTime::createFromFormat('Y-m-d\TH:i', $gueltig_bis); + $aktion = $_POST['aktion'] ?? 'speichern'; - if ($dt) { - $gueltig_bis_sql = $dt->format('Y-m-d H:i:s'); // z.B. "2025-09-03 14:00:00" + if ($aktion === 'loeschen') { + $id = (int)($_POST['id'] ?? 0); + if ($id > 0) { + $stmt = sqlsrv_query($conn, "DELETE FROM kl_hinweise WHERE id = ?", [$id]); + } } else { - die("Ungültiges Datumsformat"); - } + $nachricht = $_POST['nachricht']; + $gueltig_bis = $_POST['gueltig_bis']; // z.B. "2025-09-03T14:00" + $dt = DateTime::createFromFormat('Y-m-d\TH:i', $gueltig_bis); - if (!empty($nachricht) && !empty($gueltig_bis_sql)) { + if ($dt) { + $gueltig_bis_sql = $dt->format('Y-m-d H:i:s'); // z.B. "2025-09-03 14:00:00" + } else { + die("Ungültiges Datumsformat"); + } + + if (!empty($nachricht) && !empty($gueltig_bis_sql)) { - $stmt = sqlsrv_query($conn, - "INSERT INTO kl_hinweise (nachricht, gueltig_bis) VALUES (?, ?)", - [$nachricht, $gueltig_bis_sql] - ); + $stmt = sqlsrv_query($conn, + "INSERT INTO kl_hinweise (nachricht, gueltig_bis) VALUES (?, ?)", + [$nachricht, $gueltig_bis_sql] + ); + } } } @@ -61,6 +63,8 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){

Neuen Hinweis hinzufügen

+ +



@@ -73,7 +77,12 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){

Gültig bis: format('d.m.Y H:i') ?>
- 🗑️ Löschen + " onsubmit="return confirm('Diesen Hinweis wirklich löschen?')"> + + + + +
diff --git a/mitarbeiterverwalten.php b/mitarbeiterverwalten.php index 7f8ab4a..3c18dc4 100644 --- a/mitarbeiterverwalten.php +++ b/mitarbeiterverwalten.php @@ -2,6 +2,7 @@ include "functions.php"; +app_require_csrf(); include "header.php"; include "headerline.php"; include "nav.php"; @@ -69,6 +70,7 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){
"> + @@ -157,6 +159,7 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){ "> + @@ -210,6 +213,7 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){ echo ""; echo ""; echo ""; + echo app_csrf_field(); echo ""; echo "
  • "; @@ -218,12 +222,14 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){ echo "
    "; echo ""; echo ""; + echo app_csrf_field(); echo ""; echo "
    "; } else { echo "
    "; echo ""; echo ""; + echo app_csrf_field(); echo ""; echo "
    "; } diff --git a/namenanpassen.php b/namenanpassen.php index 280f636..088efb4 100644 --- a/namenanpassen.php +++ b/namenanpassen.php @@ -2,6 +2,7 @@ include "functions.php"; +app_require_csrf(); include "header.php"; include "headerline.php"; include "nav.php"; @@ -84,6 +85,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["aktion"]) && $_POST["a
    "> +