fix(releases): Lizenzschluessel nicht mehr im Klartext, Selbsttest, Zielorte
Vier Befunde aus einer externen Durchsicht der 2.4-Integration.
1. Die .htpasswd war eine Klartext-Kundenliste
Das htpasswd-Format hasht nur die Passwortspalte. Benutzername UND Passwort
waren der Lizenzschluessel - der Schluessel stand also im Klartext direkt
neben seinem eigenen bcrypt-Hash, und der Hash war Dekoration. Geschuetzt
hat das Ganze nur die FilesMatch-Regel in derselben Datei.
Der Benutzername wird jetzt abgeleitet: lic_<sha256(schluessel), 16 Hex>.
Die Datei enthaelt damit nur noch eine Einwegableitung und einen Hash ueber
einen hochentropen Schluessel.
Server und SDK muessen dabei zeichengenau uebereinstimmen; ein Test prueft
die C#-Ableitung gegen die PHP-Formel.
2. Ein Formatwechsel blieb unbemerkt liegen
Beim Umbau auf 1. faellt auf: reconcile() sah keinen Anlass zur
Neuerzeugung, die Dateien behielten das alte Format, waehrend die Clients
bereits das neue schickten. Die erzeugten Dateien tragen deshalb jetzt eine
Formatkennung; weicht sie ab, wird neu erzeugt.
3. Doku beschrieb Nginx, der Schutz ist Apache-only
.htaccess wird von Nginx ignoriert - dort waeren die Verzeichnisse offen und
die .htpasswd oeffentlich abrufbar. Die Statusanzeige pruefte nur, ob die
Dateien existieren, und haette in dem Fall "GESCHUETZT" gemeldet.
Neu: ein echter Selbsttest ruft die eigene Paket-Adresse OHNE Zugangsdaten
ab und erwartet 401. Er laeuft beim manuellen Erzeugen und nach jeder
automatischen Neuerzeugung; das Ergebnis steht in der Oberflaeche, ein
Fehlschlag im Log. Er findet nebenbei auch abgeschaltetes AllowOverride und
Tippfehler in der erzeugten Datei. Doku korrigiert, Nginx-Vorlage ergaenzt.
4. Erstinstallation schrieb an einen Ort, an dem Linux-Anwendungen nicht lesen
setup.json-Ziele waren immer installationsrelativ. Eine Anwendung, die sich
unter Linux richtig verhaelt, liest aus $XDG_CONFIG_HOME - /opt/<app> ist
fuer den Dienstbenutzer meist nicht schreibbar. Der Installer legte die
Datei also dorthin, wo nie jemand nachsieht.
Ziele haben jetzt ein "location": install (Vorgabe), config, data, home,
plus ${VAR}- und %VAR%-Ersetzung in "file". Unbekannte Variablen bleiben
stehen statt leer zu werden - ein Platzhalter faellt auf, ein falscher Pfad
nicht. Der Installer gibt den aufgeloesten Pfad aus, weil bei config das
Konto entscheidet, unter dem er laeuft.
Ausserdem
- Doku zeigte "status": "ok" fuer update/delete; Http::ok() erzeugt
"status": "success".
- UPGRADE §16.1 deckte Neuprodukte nicht ab: Fuer ein Produkt ohne Release
existiert /releases/<slug>/ nicht und wird uebersprungen. Das Verzeichnis
entsteht erst mit dem ersten Upload, der naechste Tick schuetzt es. Der erste
ausgelieferte Build muss die Zugangsdaten also schon mitbringen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ceb977187e
commit
7a3a5dad69
+22
-1
@@ -529,6 +529,21 @@ Läuft `cli/tick.php` als Cron, erzeugt es den Schutz beim ersten Lauf nach dem
|
||||
Deployment **von selbst**. Wer die Reihenfolge einhalten will, spielt den
|
||||
Serverteil also erst dann ein, wenn Schritt 3 erledigt ist.
|
||||
|
||||
> **Neue Produkte: die Reihenfolge oben gilt für sie nicht.**
|
||||
>
|
||||
> `ReleaseGuard` überspringt Verzeichnisse, die es nicht gibt — für ein Produkt
|
||||
> ohne Release ist `/releases/<slug>/` schlicht nicht vorhanden und damit auch
|
||||
> nicht geschützt. Das Verzeichnis entsteht erst mit dem **ersten** Upload, und
|
||||
> der nächste `tick.php`-Lauf legt den Schutz darin an.
|
||||
>
|
||||
> Es gibt also kein Zeitfenster, in dem sich ein noch ungeschütztes Release
|
||||
> herunterladen ließe, um dann das SDK nachzuziehen. **Der erste ausgelieferte
|
||||
> Build muss die Zugangsdaten bereits mitbringen** — sonst fällt die Tür hinter
|
||||
> ihm zu.
|
||||
>
|
||||
> Für Neuprodukte lautet die Reihenfolge daher schlicht: SDK 2.4 einbinden,
|
||||
> `licenseKey` übergeben, *dann* das erste Release veröffentlichen.
|
||||
|
||||
### 16.2 Was wo eingetragen wird
|
||||
|
||||
Je Produktverzeichnis eine `.htpasswd` mit den aktiven, nicht abgelaufenen
|
||||
@@ -552,9 +567,15 @@ wget -qO- https://dc.mhdf.de/installer/install.sh | sh
|
||||
|
||||
### 16.4 Prüfen
|
||||
|
||||
- [ ] **Selbsttest** im WebUI unter *UpdateService → 🔒 Zugangsschutz* — er ruft
|
||||
die eigene Paket-Adresse ohne Zugangsdaten ab und erwartet 401. Die
|
||||
Anzeige „GESCHÜTZT" allein beweist nichts: Sie prüft nur, ob die Dateien
|
||||
existieren, nicht ob der Server sie auswertet.
|
||||
- [ ] `curl -I https://dc.mhdf.de/releases/<produkt>/prod/<rid>/<version>/package.tar.gz`
|
||||
→ **401**
|
||||
- [ ] Mit `-u "<lizenzschlüssel>:<lizenzschlüssel>"` → **200**
|
||||
- [ ] Mit `-u "$(printf 'lic_%s' "$(printf %s "<schlüssel>" | sha256sum | cut -c1-16)"):<schlüssel>"` → **200**
|
||||
(Benutzername ist die Ableitung, nicht der Schlüssel — siehe
|
||||
[§5A](./UPDATESERVICE_INTEGRATION_GUIDE.md#5a-zugangsschutz-der-release-verzeichnisse))
|
||||
- [ ] `curl -I https://dc.mhdf.de/releases/<produkt>/.htpasswd` → **403**
|
||||
- [ ] `https://dc.mhdf.de/installer/update-agent-linux-x64` → **200**, weiterhin offen
|
||||
- [ ] Eine Lizenz widerrufen und erneut mit ihr laden → **401**
|
||||
|
||||
Reference in New Issue
Block a user