Sicherheits- und Stabilitaetskorrekturen nach vollstaendiger Pruefung

- Login-Sperre war umgehbar: clientKey() vertraute dem Header CF-Connecting-IP.
  Bei Cloudflare war das sicher (CF ueberschreibt ihn), auf dem eigenen Server nicht:
  der Ursprungsserver ist auch direkt unter seiner IP erreichbar, dort konnte der
  Header frei gesetzt und die 5-Versuche-Sperre komplett ausgehebelt werden
  (nachgewiesen). Jetzt req.ip hinter trust proxy.
- Absturzsicherheit: Express 4 faengt Fehler aus async-Handlern nicht ab, eine
  einzige fehlerhafte Anfrage konnte den ganzen Dienst beenden. wrap() um alle
  Handler, zentraler Fehler-Handler, unhandledRejection/uncaughtException-Netz.
- Sicherheits-Header (X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
  Permissions-Policy, HSTS) wurden bisher nur ueber die Datei _headers gesetzt,
  die auf dem eigenen Server wirkungslos ist. Jetzt im Express-Server.
- x-powered-by abgeschaltet.
- Datenschutzerklaerung/AGB: nannten Cloudflare als Hoster und eine Cloudflare-D1-
  Datenbank. Jetzt korrekt netcup (Rechenzentrum Nuernberg) als Hoster, Cloudflare
  als vorgeschaltetes CDN mit Drittlandhinweis.
This commit is contained in:
2026-08-05 22:33:31 +02:00
parent 61729d4f12
commit d6489f787a
6 changed files with 109 additions and 25 deletions
+4 -2
View File
@@ -80,7 +80,9 @@ export function gateMiddleware(req, res, next) {
if (!codesKonfiguriert) return next();
if (pfad === "/gate-auth" && req.method === "POST") {
return gateAuthHandler(req, res);
// .catch(next) ergänzt 05.08.2026: ohne das würde ein Fehler beim Signieren zu einer
// unbehandelten Promise-Ablehnung und damit zum Absturz des ganzen Servers führen.
return Promise.resolve(gateAuthHandler(req, res)).catch(next);
}
const sessionToken = req.cookies?.[COOKIE_NAME];
@@ -101,5 +103,5 @@ export function gateMiddleware(req, res, next) {
const nextParam = pfad === "/" ? "/index.html" : pfad + (req.originalUrl.includes("?") ? "?" + req.originalUrl.split("?")[1] : "");
return res.redirect(302, `/gate.html?next=${encodeURIComponent(nextParam)}`);
})();
})().catch(next); // siehe oben: verhindert Serverabsturz bei einem Fehler in der Prüfung
}
+27
View File
@@ -18,6 +18,20 @@ const PORT = Number(process.env.PORT || 4100);
const app = express();
app.set("trust proxy", 1);
app.disable("x-powered-by"); // verrät sonst unnötig den eingesetzten Technik-Stack
/* Sicherheits-Header, ergänzt 05.08.2026. Vorher standen diese in der Datei `_headers` — die
funktioniert aber NUR bei Cloudflare Pages/Netlify und wurde auf dem eigenen Server schlicht
ignoriert, d.h. die Seite lief seit dem Umzug komplett ohne diese Schutzmaßnahmen (nachgeprüft
an den Live-Antwortheadern). */
app.use((req, res, next) => {
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
res.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=(), payment=()");
res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
next();
});
app.use(cookieParser());
app.use(express.json({ limit: "1mb" }));
@@ -31,6 +45,19 @@ app.use((req, res) => {
});
});
/* Fehler-Handler (muss NACH allen Routen stehen), ergänzt 05.08.2026: antwortet mit einer
schlichten Meldung statt einer Express-Standardseite mit komplettem Stacktrace und Dateipfaden. */
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
console.error(`[fehler] ${req.method} ${req.originalUrl}:`, err?.stack || err);
if (res.headersSent) return;
res.status(500).send("Es ist ein Fehler aufgetreten. Bitte später erneut versuchen.");
});
/* Letztes Netz: Ein unbehandelter Fehler darf die Website nicht offline nehmen. */
process.on("unhandledRejection", (grund) => console.error("[unhandledRejection]", grund));
process.on("uncaughtException", (fehler) => console.error("[uncaughtException]", fehler?.stack || fehler));
app.listen(PORT, "127.0.0.1", () => {
console.log(`DogFather Universe Server läuft auf http://127.0.0.1:${PORT}`);
});