Strukturen sicher hosten
So teilen Sie ein D2H-Web-Paket mit einem Kunden über das Internet — ohne es allen anderen zugänglich zu machen.
Eine Struktur ist kein gewöhnlicher Web-Inhalt
Wenn D2H ein Web-Paket exportiert, entsteht eine statische Microsite: ein durchklickbarer Baum der wiederhergestellten Dateien, ganz ohne Backend. Der Kunde öffnet sie und sieht genau, was wiederhergestellt wurde — bevor er bezahlt.
Doch diese Dateiliste ist privat. Sie enthält die Namen der Dokumente, Fotos und Geschäftsdaten eines Kunden. Sie darf also nicht öffentlich erreichbar sein, nicht auf einer erratbaren Adresse liegen, nicht von Suchmaschinen indexiert werden, und der Zugriff muss zeitlich begrenzt und widerrufbar sein.
Der ganze Leitfaden in einem Satz
Liefern Sie die statischen Dateien niemals direkt aus — stellen Sie immer ein schlankes Gateway davor, das einen signierten Link prüft und die Datei erst dann freigibt.
Der Aufbau auf einen Blick
Drei unabhängige Teile. Jeden können Sie für sich betrachten.
Erzeugen
D2H erstellt eine statische Microsite und packt sie in ein .tar.gz, benannt nach der Auftragsnummer.
Übertragen & entpacken
Der Techniker lädt per chroot-SFTP hoch; ein Cron-Job prüft das Archiv und entpackt es ins Datenverzeichnis.
Sicher ausliefern
Ein schlankes Gateway prüft einen signierten, zeitlich begrenzten Link und übergibt die Datei per internem Redirect an den Webserver.
Schritt A — Die erzeugte Microsite
Die D2H-Ausgabe ist rein statisch — HTML, JS und Daten, kein Backend, keine Datenbank, keine ausgehenden Aufrufe. Das ist Absicht: statischer Inhalt bietet kaum Angriffsfläche, und ein Archiv läuft überall.
Regeln für das Archiv
- —Benennen Sie die Datei nach der Auftragsnummer: 12345.tar.gz (ein optionaler Zusatz wie 12345-Data.tar.gz ist erlaubt).
- —Legen Sie die Dateien im Wurzelverzeichnis des Archivs ab (index.html, data/ …) — kein verschachtelter Ordner darin.
- —Ein neues Archiv mit derselben Nummer ersetzt die vorherige Version.
12345.tar.gz ├── index.html # the whole viewer, self-contained ├── data/ # compressed file list, loaded on demand ├── pako.min.js # the only JS dependency └── logo.svg
Halten Sie die Seite in sich geschlossen
- —Keine externen Abhängigkeiten — CDNs, Schriftarten, Analytics. Jede ausgehende Anfrage verrät einem Dritten, wer sich was ansieht. Alles inline oder lokal halten.
- —Keine internen Metadaten in den veröffentlichten Dateien — Festplattenpfade vom Rechner des Technikers, Namen, interne IDs (siehe done.json-Hinweis unten).
- —Verweisen Sie auf Assets relativ (data/…, pako.min.js), damit die Seite hinter dem Gateway unter einem Token funktioniert.
Schritt B — Übertragen und entpacken
Upload per chroot-SFTP (ohne Shell)
Legen Sie einen eigenen Systembenutzer nur für Uploads an, eingesperrt (chroot) im Datenverzeichnis und mit deaktivierter Shell. Selbst wenn das Passwort durchsickert, kann der Angreifer nur eine Datei in einen Ordner ablegen — der Rest des Servers ist unerreichbar. Uploads landen in einem Staging-Ordner, nicht im ausgelieferten Verzeichnis, sodass eine unvollständige Datei einem Kunden nie angezeigt wird.
sftp d2h-upload@structures.example.com cd upload put 12345.tar.gz quit
Per Cron entpacken (prüfen, dann in Quarantäne)
Ein Cron-Job (etwa alle 5 Minuten) überwacht den Staging-Ordner und prüft jedes Archiv gegen eine Whitelist, bevor er entpackt:
- —Der Name muss eine saubere Auftragsnummer sein (optional mit erlaubtem Zusatz).
- —Die Nummer muss in Ihrem erlaubten Auftragsnummern-Bereich liegen — das weist Tippfehler und gefälschte Namen ab.
- —Ungültige Dateien werden nicht gelöscht — sie werden in einen Quarantäne-Ordner verschoben (mit Zeitstempel-Präfix) zur späteren Prüfung. Löschen würde die Spur eines Fehlers oder Angriffs verwischen.
- —Jede Aktion wird protokolliert: was, wann und warum abgelehnt.
Schritt C — Sichere Auslieferung über das Gateway
Die Datendateien sind nie direkt erreichbar. In nginx ist das Datenverzeichnis als internal markiert (von außen nicht erreichbar). Zum Internet zeigt nur ein schlankes Gateway, das einen signierten Token (oder ein signiertes Cookie) prüft und nginx dann per X-Accel-Redirect anweist, die Datei zu senden.
Signierte Links (HMAC, zeitlich begrenzt)
Ihr Informationssystem erzeugt einen Link, der kryptografisch mit einem gemeinsamen Secret signiert ist. Der Token ist kein zufälliger String in einer Datenbank — er ist ein HMAC, sodass das Gateway ihn durch Nachrechnen prüft, ganz ohne Speicherung.
- —Signiert, nicht erratbar — ohne das Secret lässt sich kein gültiger Link fälschen.
- —Zeitlich begrenzt — das Ablaufdatum ist Teil der Signatur, ein alter Link lässt sich also nicht verlängern.
- —Zustandslos — das Gateway schlägt nichts nach; es rechnet nur den HMAC nach und vergleicht.
// Generate the link (in your information system)
// shape: /v/{number}-{expiry}-{hmac12}
$expiry = time() + 86400; // 24 h for a direct link
$hmac = substr(hash_hmac('sha256',
$orderNumber . '.' . $expiry, $secret), 0, 12);
$url = 'https://structures.example.com/v/'
. $orderNumber . '-' . $expiry . '-' . $hmac;
// Verify at the gateway — same computation, constant-time compare
$expected = substr(hash_hmac('sha256',
$orderNumber . '.' . $expiry, $secret), 0, 12);
if ($expiry < time() || !hash_equals($expected, $hmac)) {
http_response_code(403);
exit;
}Bevor Sie den X-Accel-Redirect-Pfad bilden, prüfen Sie die Auftragsnummer aus der URL als rein numerisch. Das Parsen von /v/{nummer}-… erzwingt das bereits, doch es ausdrücklich zu nennen schützt vor Path Traversal.
Empfohlene Link-Gültigkeiten
| Interne Vorschau (Techniker / Büro, aus dem IS) | 24 Stunden |
| Per E-Mail an den Kunden gesendeter Link | 14 Tage |
Signierte Cookies für Assets (keine PHP-Session)
Die Microsite lädt weitere Dateien (data/…, JS). Damit das Gateway nicht in jeder URL einen Token braucht, setzt es nach Prüfung des Einstiegslinks ein signiertes Cookie; die Assets werden dann durch dieses Cookie autorisiert. Keine PHP-Session — kein serverseitiger Zustand, der durchsickern oder aufgeräumt werden müsste, und es skaliert ohne gemeinsamen Session-Speicher.
Nur-Lese-Datenbankbenutzer
Falls das Gateway einen Auftrag gegen Ihre Datenbank prüft, verwenden Sie ein separates Konto mit nur SELECT auf die nötigen Tabellen — niemals den produktiven IS-Benutzer. Wird das Gateway je kompromittiert, lässt sich nichts schreiben oder löschen.
nginx — die Kernidee
server {
server_name structures.example.com;
# add_header does NOT inherit — repeat it in every nested location
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
add_header Strict-Transport-Security "max-age=31536000" always;
# data directory — unreachable from outside, only via X-Accel-Redirect
location /internal-data/ {
internal;
alias /var/www/structures-data/;
add_header Cache-Control "no-cache" always;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
# everything else goes through the gateway
location / {
try_files $uri /index.php?$query_string;
}
# block done.json — D2H metadata with internal paths, the viewer never uses it
location ~ /done\.json$ { return 404; }
}Das Datenverzeichnis ist internal — erreichbar nur über den X-Accel-Redirect des Gateways. Beachten Sie: add_header wird in nginx nicht vererbt; sobald eine verschachtelte location ein eigenes add_header setzt, müssen dort alle Header wiederholt werden.
Bedrohungsmodell
Was schiefgehen kann und was es verhindert.
| Bedrohung | Gegenmaßnahme |
|---|---|
| URL erraten (/12345/) | Es gibt keinen direkten Pfad — nur /v/<token> mit HMAC-Signatur. |
| Ein gestohlener Link lebt ewig | Das Ablaufdatum ist Teil der Signatur; das Gateway weist einen abgelaufenen Link ab. |
| Indexierung durch Suchmaschinen / Scanner | X-Robots-Tag: noindex und robots.txt: Disallow: /. |
| Direkter Zugriff auf die Dateien | nginx internal + X-Accel-Redirect — alles läuft über das Gateway. |
| Preisgabe interner Metadaten | Für done.json 404 zurückgeben (siehe unten). |
| Kompromittiertes Upload-Konto | SFTP chroot + keine Shell, Upload nur in einen Staging-Ordner. |
| Gateway-Kompromittierung → DB-Schreibzugriff | Das DB-Konto des Gateways ist nur SELECT. |
| MITM / Downgrade | HTTPS (z. B. Let’s Encrypt) + HSTS. |
| Ein CDN cacht private Daten | Das CDN für diese Domain im Nur-DNS-Modus betreiben (ohne Proxy). |
| Browser hält eine alte Version | Cache-Control: no-cache auf den Datendateien. |
Eine Datei immer sperren: done.json
D2H schreibt neben die Struktur ein done.json-Manifest. Es enthält source_dir — den absoluten Pfad auf dem Rechner des Technikers. Der Viewer nutzt diese Datei nicht, doch wird sie ausgeliefert, gibt sie interne Informationen preis (Rechner- und Benutzernamen, interne Datenstruktur). Geben Sie für done.json am Gateway 404 zurück oder löschen Sie sie beim Entpacken des Archivs.
Das empfohlene Rezept
Ein wiederverwendbares Muster, wann immer Sie privaten, erzeugten Inhalt hosten.
- 1Erzeugen Sie statische Ausgabe ohne ausgehende Abhängigkeiten — alles inline oder lokal.
- 2Trennen Sie Speicherung von Auslieferung — markieren Sie das Datenverzeichnis als internal, exponieren Sie nur ein schlankes Gateway.
- 3Gewähren Sie Zugriff nur über einen signierten, zeitlich begrenzten Link — HMAC-SHA256 über {id}.{ablauf} mit einem Secret, gekürzt (12 Zeichen genügen). Halten Sie das Secret aus dem Repository heraus.
- 4Nach Prüfung des Tokens X-Accel-Redirect (nginx) / X-Sendfile (Apache) verwenden — das Gateway liest die Datei nie selbst, es weist den Server nur an, sie zu senden.
- 5Autorisieren Sie Assets mit einem signierten Cookie, nicht mit einer PHP-Session.
- 6Sperren Sie Uploads ab — ein eigenes SFTP-Konto, chroot + keine Shell, in einen Staging-Ordner.
- 7Prüfen Sie die Eingabe gegen eine Whitelist (Namensformat + Nummernbereich); ungültige Dateien in Quarantäne, nicht löschen.
- 8Deaktivieren Sie die Indexierung (X-Robots-Tag, robots.txt) und erzwingen Sie HTTPS + HSTS.
- 9Verwenden Sie vom Gateway aus ein Nur-Lese-DB-Konto.
- 10Betreiben Sie jedes CDN für den privaten Inhalt im Nur-DNS-Modus.
- 11Prüfen Sie genau, was die veröffentlichten Dateien enthalten — keine internen Pfade oder Metadaten (done.json immer sperren).
Checkliste vor dem Start
- Die Microsite stellt keine externen Anfragen (im Netzwerk-Tab des Browsers prüfen).
- done.json gibt 404 zurück (oder wird beim Entpacken gelöscht).
- Das Archiv ist <nummer>.tar.gz mit Dateien im Archiv-Wurzelverzeichnis.
- Ein direktes GET /internal-data/… gibt 404 zurück.
- GET /<nummer>/ ist ohne Token nicht erreichbar.
- Ein gültiges /v/<token> funktioniert; nach Ablauf gibt es 403/404 zurück.
- Ein gefälschter HMAC gibt 403/404 zurück.
- robots.txt ist Disallow: /; Antworten tragen X-Robots-Tag: noindex und HSTS.
- Das SFTP-Konto ist chroot + keine Shell (ein ssh-Versuch muss scheitern).
- Das DB-Konto des Gateways kann nur SELECT (ein INSERT muss scheitern).
- Jedes CDN ist für diese Domain Nur-DNS.
- Secrets und Passwörter liegen nur in der serverseitigen Konfiguration, außerhalb des Repositories.
Was Sie nicht tun sollten
- Eine Struktur auf einer öffentlichen, erratbaren URL ausliefern (/orders/12345/).
- Sich auf „niemand kennt die URL“ verlassen statt auf eine kryptografische Signatur.
- Ein zufälliger Token in der Datenbank als einziger Schutz (zusätzlicher Zustand; sickert mit der DB durch).
- Den Webserver das Datenverzeichnis direkt ausliefern lassen, ohne Gateway.
- Interne Metadaten in den veröffentlichten Dateien (done.json!).
- Externe Abhängigkeiten in der Seite (CDN, Analytics).
- Ein produktives Schreib-DB-Konto im Gateway statt eines Nur-Lese-Kontos.
- Ein CDN-Proxy, der privaten Inhalt cacht.
- Ungültige Uploads löschen, statt sie in Quarantäne zu stellen.
Automatisieren
Jede Funktion der Oberfläche ist skriptbar.
Verarbeiten Sie Aufträge im Stapel, binden Sie D2H in Ihren Annahme-Ablauf ein oder betreiben Sie es auf einem Headless-Server. Jedes Web-Paket enthält ein maschinenlesbares Manifest (done.json) mit Summen und Version — ideal für Watch-Folder-Automatisierung auf der Hosting-Seite.
$ d2h --dir /recovered/case-1042 --output /srv/catalogs --lang en --html D2H v1.6.1 Scanning: /recovered/case-1042 Found 239,556 files in 30,818 folders (18.7 GB) Generating web output... Archive: /srv/catalogs/case-1042.tar.gz (48.2 MB) HTML: /srv/catalogs/case-1042.html Done!
Gängige Optionen
--dir--output--html--title--lang--theme--logo--hidden--include-empty-dirs--follow-symlinks--configEinmal ausrollen, pro Standort konfigurieren
Legen Sie eine d2h.config.json neben die ausführbare Datei (perfekt für eine Netzwerkfreigabe oder einen USB-Stick), und jeder Techniker erhält dieselben Voreinstellungen: Branding, Ausgabepfade und Regex-Regeln, die Ihre Auftragsnummerierung erkennen und Sprache sowie Ziel je Auftrag automatisch wählen. Keine Installation, keine Einrichtung pro Rechner.
{
"branding": { "logoPath": "our-logo.png", "headerBackground": "#0a3d62" },
"defaults": { "language": "de", "webOutputDir": "\\\\nas\\catalogs" },
"jobId": {
"pattern": "JOB-\\d{4}",
"rules": [
{ "match": "^JOB-9", "language": "de", "webOutputDir": "\\\\nas\\catalogs-de" }
]
}
}Technische Hinweise
- —Native Desktop-Anwendung (Rust + Tauri) — kein Web-Dienst.
- —Die Ausgabe ist statisches HTML/JS/CSS — kein Backend, keine Datenbank, läuft überall.
- —Web-Paket: index.html plus komprimierte (gzip) Daten, bei Bedarf nachgeladen.
- —Einzeldatei-Export: alle Daten in einer HTML-Datei eingebettet (gzip + base64), funktioniert offline.
- —Suchindex und Baum werden beim Export erstellt; das Durchsuchen läuft vollständig clientseitig.
- —Der Katalog enthält nur Metadaten — Dateinamen, Größen und Änderungsdaten. Niemals Dateiinhalte.
Was in einem Web-Paket steckt
case-1042.tar.gz ├── index.html # the whole viewer, self-contained ├── data/ │ ├── chunk_0.json.gz # directory data, on demand │ ├── … │ └── chunk_index.json.gz ├── full_index.json.gz # search index (lazy) ├── pako.min.js # the only JS dependency ├── logo.svg └── done.json # machine-readable manifest
Zurück zu D2H
Dieser Aufbau teilt die von D2H erzeugten Web-Pakete. Wie sie entstehen, zeigt das Tool selbst.
D2H-Übersicht