KRITISCH: Server-Quellcode + kompletter Git-Verlauf waren oeffentlich abrufbar

Sicherheits-Audit vor dem geplanten oeffentlichen Start morgen (21.08.2026,
Nutzer-Anfrage: "duerfen die leute keinen zugriff auf veraenderungen haben").
SITE_DIR ist der GESAMTE Repo-Ordner (join(__dirname, "..")), express.static
lieferte daher nicht nur die Website aus, sondern auch:
- server/ (inkl. gate.js, das komplette Sicherheitskonzept im Klartext)
- server-internal/ (Admin-/Supporter-Backend-Quellcode)
- cloudflare-worker/ (altes Backend)
- .git/ (VOLLSTAENDIGE Commit-Historie, rekonstruierbar per Git-Dump)
- CLAUDE.md, DEPLOY.md, wrangler.toml, netlify.toml, gate-worker.js,
  gate.html.bak-07-08-2026 (Alt-Backup-Datei einer frueheren Session)

Live nachgewiesen (mit gueltigem Zugangscode -- morgen faellt die Schranke
fuer ALLE weg): /server/gate.js und /.git/config lieferten HTTP 200.
Ursache: serve-static blockt per Default nur Dateien, deren EIGENER Name
mit einem Punkt beginnt (server/.env -> zufaellig schon 404), aber NICHT
rekursiv -- .git/config wird trotzdem ausgeliefert, weil "config" selbst
nicht mit einem Punkt beginnt, nur der Ordner davor.

Fix: eigene Sperr-Middleware VOR express.static, unabhaengig von
gateMiddleware (bleibt also auch nach dem Entfernen der Zugangsschranke
wirksam). Blockt ganze Ordner (server/, server-internal/,
cloudflare-worker/) + versteckte Ordner/Dateien rekursiv (jedes
Pfadsegment, das mit "." beginnt, ausser .well-known) + eine feste Liste
an Alt-Dateien + jedes *.bak-Muster, damit auch kuenftige Backup-Reste
automatisch mitgeschuetzt sind.

Lokal mit echtem Express-Server verifiziert (gateMiddleware absichtlich
deaktiviert, um exakt den morgigen "oeffentlich"-Zustand zu simulieren):
alle vorher gefundenen Luecken jetzt 404, alle echten Seiten/Assets
(index.html, main.css, main.js, manifest.json, robots.txt, favicon)
weiterhin 200.

Getrennt prooft: server-internal/ (eigener Dienst unter
postfach.dogfather-universe.com, Port 4200) hat sein EIGENES,
unabhaengiges Session-System -- jede /admin/*-Route ist einzeln per
requireTeamSession-Middleware abgesichert (in index.js durchgezaehlt,
keine Ausnahme gefunden), live mit einer unauthentifizierten Anfrage
gegen /admin/users/list bestaetigt (401). Dieser Dienst war nie vom
Website-Gate abhaengig und ist von diesem Fund nicht betroffen.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-20 12:47:36 +02:00
co-authored by Claude Opus 5
parent 1d28d8db2b
commit 9adebd9bc4
+34
View File
@@ -37,6 +37,40 @@ app.use(cookieParser());
app.use(express.json({ limit: "1mb" }));
app.use(gateMiddleware);
/* Sicherheits-Fix 20.08.2026 (Audit vor dem geplanten öffentlichen Start morgen).
SITE_DIR ist der GESAMTE Repo-Ordner (join(__dirname, "..")) — express.static liefert
ohne diese Sperre daher nicht nur die eigentliche Website aus, sondern auch den Server-
Quellcode (server/, server-internal/, cloudflare-worker/) und das komplette
.git-Verzeichnis samt Commit-Historie.
Live nachgewiesen (mit gültigem Zugangscode, der morgen für alle wegfällt):
/server/gate.js und /.git/config lieferten HTTP 200. Ursache: serve-static blockt per
Voreinstellung nur Dateien, deren EIGENER Name mit einem Punkt beginnt (server/.env ->
404 durch diese Regel) — aber NICHT rekursiv. Eine Datei wie .git/config wird trotzdem
ausgeliefert, weil "config" selbst nicht mit einem Punkt beginnt, nur der Ordnername davor.
Diese Sperre läuft daher bewusst unabhängig von gateMiddleware (bleibt auch nach dem
morgigen Entfernen der Zugangsschranke wirksam) und blockt ganze Ordner/Muster statt
einzelner Dateien, damit neue Dateien darin automatisch mitgeschützt sind. */
const GESPERRTE_ORDNER = ["/server", "/server-internal", "/cloudflare-worker"];
const GESPERRTE_DATEIEN = [
"/CLAUDE.md", "/DEPLOY.md", "/wrangler.toml", "/netlify.toml",
"/gate-worker.js", "/_headers", "/.gitignore", "/.nojekyll", "/.assetsignore",
];
app.use((req, res, next) => {
const pfad = req.path;
const hatVerstecktenOrdner = pfad
.split("/")
.some((teil) => teil.startsWith(".") && teil.length > 1 && teil !== ".well-known");
const inGesperrtemOrdner = GESPERRTE_ORDNER.some((p) => pfad === p || pfad.startsWith(p + "/"));
const istGesperrteDatei = GESPERRTE_DATEIEN.includes(pfad);
const istBackupDatei = /\.bak(-|\.|$)/i.test(pfad);
if (hatVerstecktenOrdner || inGesperrtemOrdner || istGesperrteDatei || istBackupDatei) {
return res.status(404).sendFile(join(SITE_DIR, "404.html"), (err) => {
if (err) res.status(404).send("Nicht gefunden.");
});
}
next();
});
app.use(express.static(SITE_DIR, { index: "index.html" }));
app.use((req, res) => {