Sicherheit
- install_db.php war ohne Authentifizierung erreichbar und setzte bei jedem
Aufruf das Admin-Passwort auf einen fest im Code stehenden Wert zurück.
Jetzt Auth-Pflicht; ein Konto wird nur bei leerer Benutzertabelle angelegt.
- Stored XSS im Bugtracker-Detail-Modal: Titel, Beschreibung, Fehlermeldung,
Stacktrace und Kommentare gingen ungefiltert durch innerHTML.
- report.php, projects.php und das Veröffentlichen von Releases verlangen jetzt
zwingend ein Token. Publish war zuvor völlig ungeschützt.
- CSRF-Token in allen Formularen, Session-Regenerierung nach Login,
Drosselung fehlgeschlagener Anmeldeversuche.
- Zugangsdaten aus der Versionskontrolle entfernt (Serverdaten.txt,
config.php, .htpasswd, deploy_config.json). Historie enthält sie weiterhin,
Rotation erforderlich (siehe docs/UPGRADE.md).
- Token-Validierung nur noch über SHA-256-Hash; expires_at wird ausgewertet.
Behobene 500er
- Audit::log() war in index.php weder eingebunden noch importiert. Jeder
Klick auf "Aktivierung freigeben" endete in einem Fatal Error.
- Derselbe benannte PDO-Platzhalter mehrfach je Statement (:id in
revokeToken/deleteToken, :q siebenfach in der Volltextsuche). Bei
EMULATE_PREPARES=false ist das nicht zulässig und warf HY093.
- Migration 005 nutzte dynamisches SQL, dessen Semikolons in String-Literalen
vom alten explode(';')-Installer als Statement-Ende gelesen wurden. Sie
schlug still fehl, wodurch push_id/target_agent/tags dauerhaft fehlten.
- Monitor-Umbenennung ohne Transaktion, verschachtelte Transaktionen im
RateLimiter.
Funktionale Korrekturen
- Der Watchdog-Evaluator fehlte vollständig: Monitor-Zustände änderten sich nur
beim Eintreffen eines Heartbeats, ein ausgefallenes System blieb dauerhaft
"up". Erster Lauf auf dem Produktivsystem: 7 von 10 Monitoren waren
tatsächlich seit über einem Tag nicht erreichbar.
- Das Feld "os" fehlte im Monitor-Dialog, wurde aber gespeichert und löschte
damit bei jedem Speichern das Betriebssystem.
- Der Resolve-Dialog existierte im HTML nicht; der Button war funktionslos.
- Versionsvergleich erfolgte lexikografisch, wodurch 1.9.0 als neuer galt
als 1.10.0.
- Schreiboperationen meldeten Erfolg auch für nicht existierende IDs.
- Post/Redirect/Get gegen doppelte Einträge beim Neuladen.
Neue Struktur
- src/bootstrap.php mit PSR-4-Autoloader ersetzt die require-Ketten.
- Core: Config, Http, Csrf, ApiAuth, Logger, Migrator, ErrorReporter.
- Migrator mit zeichenweisem SQL-Parser, dc_migrations und Baseline-Verfahren,
damit bestehende Installationen keine Beispieldaten zurückbekommen.
Agenten-Workflow
- Claim/Lease: Items werden exklusiv übernommen, damit nicht zwei Agenten am
selben Problem arbeiten. action=next holt und reserviert in einem Zug.
- Idempotenz über client_ref, Deduplizierung auch für Feature Requests,
Erkennung von Regressionen, automatische Eskalation des Schweregrads.
- Strukturierter Code-Kontext (repo_url, commit_sha, file_path, line_no).
- Delta-Abfragen über updated_since, Pagination, Bulk-Update.
- Beim Veröffentlichen eines Releases schließen sich Items mit passendem
resolved_in_build selbst.
- Ausgehende Webhooks mit HMAC-Signatur, /api/health, /api/openapi.json.
- Unbehandelte Fehler meldet die Plattform in ihren eigenen Bugtracker.
WebUI
- Serverseitige Filterung mit Pagination statt Rendern aller Datensätze.
- Migrations-Schranke, Evaluator-Warnung, Übersicht aktiver Agenten.
Zeitstempel liegen in der Datenbank durchgängig in UTC und werden für die
Anzeige in die App-Zeitzone umgerechnet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.1 KiB
Umstellung auf Version 2.0 — Ablaufplan
Diese Fassung enthält Sicherheitskorrekturen, die das Verhalten der Schnittstellen ändern. Bitte in dieser Reihenfolge vorgehen.
1. Vor dem Deployment: Zugangsdaten wechseln
Serverdaten.txt, config/config.php, config/.htpasswd und
scripts/deploy_config.json lagen im Git-Repository. Sie sind jetzt per
.gitignore ausgeschlossen und aus dem Index entfernt — die Git-Historie
enthält sie aber weiterhin. Alle betroffenen Zugangsdaten sind daher als
kompromittiert zu behandeln:
- MySQL-Passwort ändern, danach in
config/config.phpeintragen - FTP-Passwort ändern, danach in
scripts/deploy_config.jsoneintragen .htpasswd-Passwort fürdeployneu setzen- Git-Token
eb429575…widerrufen und neu ausstellen - Admin-Passwort im WebUI ändern (die alte Fassung setzte es bei jedem
Aufruf von
install_db.phpaufAdmin1337!zurück — jeder im Internet konnte das auslösen)
Wenn die Historie bereinigt werden soll, geht das mit
git filter-repo. Das schreibt
alle Commit-Hashes um; bei einem Repository mit mehreren Nutzern vorher abstimmen.
2. Konfiguration ergänzen
config/config.php braucht drei neue Schlüssel unter security. Die
mitgelieferte Datei enthält bereits erzeugte Werte; für eine neue Installation:
cp config/config.example.php config/config.php
openssl rand -hex 32 # je einmal für shared_key, webhook_key, license_key
| Schlüssel | Zweck |
|---|---|
security.shared_key |
Server-zu-Server-Aufrufe: Evaluator-Cron, Migration, Deaktivierung |
security.webhook_key |
HMAC-Signatur ausgehender Webhooks |
security.license_key |
Signatur der Offline-Lizenzdateien (.lic) |
app.debug |
Auf Produktivsystemen false — steuert, ob Exception-Texte ausgeliefert werden |
Der bisherige
shared_key(DC_MASTER_SECURE_TOKEN_2026_x98f) stand im Repository und wurde ersetzt. Wer ihn irgendwo eingetragen hat — etwa für/api/license/v1/deactivate— muss den neuen Wert nachziehen.
3. Deployment
python scripts/deploy.py
Das Skript überträgt unter anderem die neuen Verzeichnisse var/ (Logs) und
die zusätzlichen .htaccess-Dateien in config/, src/ und sql/.
4. Migration ausführen
Im WebUI anmelden, dann System → DB-Migration → Migration jetzt ausführen.
Alternativ über die Kommandozeile:
php public/install_db.php
Oder mit dem Shared Key:
curl -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/install_db.php
Die Migration ist additiv und legt an bzw. korrigiert:
dc_migrations— vermerkt angewendete Versionen, damit nichts doppelt läuftdc_login_attempts— Drosselung fehlgeschlagener Anmeldungendc_webhooks— ausgehende Benachrichtigungen- Bugtracker: Claim/Lease,
client_ref,dedup_key, Code-Kontextfelder, Indizes - Watchdog:
last_state_change_utc,down_since_utc, Zustandunknown - Reparatur von Migration 005 — deren Spalten (
push_id,target_agent,tags) fehlten bisher auf Datenbanken, die aus Migration 004 stammen. Die alte Fassung nutzte dynamisches SQL, dessen Semikolons in String-Literalen vom damaligen Installer als Statement-Ende gelesen wurden; die Fehler wurden stillschweigend verschluckt. - Reparatur der Token-Hashes — die Validierung vergleicht jetzt nur noch den SHA-256-Hash. Die geseedeten Beispiel-Tokens trugen Hashes, die nicht zu ihrem Klartext passten; sie werden korrigiert, damit bestehende Tokens weiterhin funktionieren.
5. Cron für den Watchdog-Evaluator einrichten
Ohne diesen Schritt sind die Monitor-Zustände wertlos. Der Evaluator fehlte bisher vollständig — der Zustand änderte sich nur beim Eintreffen eines Heartbeats, ein ausgefallener Server blieb dauerhaft grün.
* * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/api/watchdog/v1/evaluate > /dev/null
Solange der Job fehlt, zeigt das WebUI oben einen Warnhinweis mit einer Schaltfläche für einen einmaligen Lauf.
6. Agenten-Tokens ausstellen
Die Ingest-Endpunkte verlangen jetzt zwingend ein Token.
- WebUI → Token-Verwaltung → Master-Token erstellen
- Scopes wählen (für einen Coding-Agenten:
bugtracker:report,bugtracker:read,bugtracker:manage) - Master-Token einmalig kopieren und auf dem Agenten-Rechner als
DC_TOKENhinterlegen — oder den Agenten per/api/tokens/v1/provisionein eigenes Sub-Token ziehen lassen
7. Bestehende Integrationen anpassen
| Betroffen | Was zu tun ist |
|---|---|
Aufrufe von /api/bugtracker/v1/report ohne Token |
Token-Header ergänzen |
Aufrufe von /api/bugtracker/v1/projects ohne Token |
Token-Header ergänzen |
| Skripte, die Releases veröffentlichen | Token mit updateservice:publish ergänzen |
| Auswertung der Antworten | Neues Format: {"status":"success",…} bzw. {"status":"error","error":{"code":…}} |
Watchdog-Agenten mit wd_live_…-Token |
Laufen unverändert weiter |
Clients, die /api/updateservice/v1/check aufrufen |
Unverändert, weiterhin ohne Token |
Clients, die /api/license/v1/validate aufrufen |
Unverändert, weiterhin ohne Token |
8. Zeitzonen
Datenbankzeitstempel liegen jetzt durchgängig in UTC; das WebUI rechnet für
die Anzeige in app.timezone (Europe/Berlin) um. Vorhandene Datensätze wurden
in Serverzeit geschrieben und erscheinen daher einmalig um den Zeitzonenversatz
verschoben. Für Monitoring-Daten ist das ohne Bedeutung, für den Audit-Log
gegebenenfalls beachten.
9. Prüfen, ob alles läuft
curl https://dc.mhdf.de/api/health -H "Authorization: Bearer <SHARED_KEY>"
Erwartet wird "healthy": true, eine leere schema.pending-Liste und ein
checks.evaluator.ok von true.
Zusätzlich stichprobenartig im WebUI prüfen:
- Anmeldung funktioniert
- Projekt anlegen und wieder löschen
- Monitor bearbeiten — das Feld Betriebssystem bleibt nach dem Speichern erhalten (wurde zuvor bei jedem Speichern geleert)
- Bugtracker: „🔍 Details" öffnet den Dialog, „✔" öffnet den Lösen-Dialog (dessen HTML fehlte bisher komplett)
- Token widerrufen und löschen (warf zuvor
HY093) - Lizenz-Aktivierung freigeben (warf zuvor
Class "Audit" not found) - Nach dem Speichern F5 drücken — es entsteht kein zweiter Eintrag mehr
10. Optional: Webhooks
Ereignisgesteuerte Benachrichtigung statt Polling. Ziel direkt in der Datenbank eintragen:
INSERT INTO dc_webhooks (name, url, project_slug, events, secret, enabled)
VALUES ('Telegram Alarm', 'https://n8n.example.com/webhook/dc',
NULL, 'bug.critical,monitor.down', 'geheimnis', 1);
Verfügbare Ereignisse: bug.created, bug.critical, bug.resolved,
feature.created, monitor.down, monitor.recovered, release.published,
oder * für alle.
Jede Zustellung trägt eine Signatur:
X-DC-Timestamp: 1754563200
X-DC-Signature: sha256=<hex(hmac_sha256(secret, timestamp + "." + body))>
Nach 20 Fehlversuchen in Folge deaktiviert sich ein Webhook selbst.