Zum Hauptinhalt springen

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.

Ein Live-Beispiel der Ausgabe ansehen

Der Aufbau auf einen Blick

Drei unabhängige Teile. Jeden können Sie für sich betrachten.

A

Erzeugen

D2H erstellt eine statische Microsite und packt sie in ein .tar.gz, benannt nach der Auftragsnummer.

B

Übertragen & entpacken

Der Techniker lädt per chroot-SFTP hoch; ein Cron-Job prüft das Archiv und entpackt es ins Datenverzeichnis.

C

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.
gateway.php
// 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 Link14 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

nginx.conf
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.

BedrohungGegenmaßnahme
URL erraten (/12345/)Es gibt keinen direkten Pfad — nur /v/<token> mit HMAC-Signatur.
Ein gestohlener Link lebt ewigDas Ablaufdatum ist Teil der Signatur; das Gateway weist einen abgelaufenen Link ab.
Indexierung durch Suchmaschinen / ScannerX-Robots-Tag: noindex und robots.txt: Disallow: /.
Direkter Zugriff auf die Dateiennginx internal + X-Accel-Redirect — alles läuft über das Gateway.
Preisgabe interner MetadatenFür done.json 404 zurückgeben (siehe unten).
Kompromittiertes Upload-KontoSFTP chroot + keine Shell, Upload nur in einen Staging-Ordner.
Gateway-Kompromittierung → DB-SchreibzugriffDas DB-Konto des Gateways ist nur SELECT.
MITM / DowngradeHTTPS (z. B. Let’s Encrypt) + HSTS.
Ein CDN cacht private DatenDas CDN für diese Domain im Nur-DNS-Modus betreiben (ohne Proxy).
Browser hält eine alte VersionCache-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.

  1. 1Erzeugen Sie statische Ausgabe ohne ausgehende Abhängigkeiten — alles inline oder lokal.
  2. 2Trennen Sie Speicherung von Auslieferung — markieren Sie das Datenverzeichnis als internal, exponieren Sie nur ein schlankes Gateway.
  3. 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.
  4. 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.
  5. 5Autorisieren Sie Assets mit einem signierten Cookie, nicht mit einer PHP-Session.
  6. 6Sperren Sie Uploads ab — ein eigenes SFTP-Konto, chroot + keine Shell, in einen Staging-Ordner.
  7. 7Prüfen Sie die Eingabe gegen eine Whitelist (Namensformat + Nummernbereich); ungültige Dateien in Quarantäne, nicht löschen.
  8. 8Deaktivieren Sie die Indexierung (X-Robots-Tag, robots.txt) und erzwingen Sie HTTPS + HSTS.
  9. 9Verwenden Sie vom Gateway aus ein Nur-Lese-DB-Konto.
  10. 10Betreiben Sie jedes CDN für den privaten Inhalt im Nur-DNS-Modus.
  11. 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.

terminal
$ 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--config

Einmal 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.

d2h.config.json
{
  "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