Härte eine Node-API für die Produktion: korrektes CORS, Rate Limiting, Security-Header und Abwehr gegen die wichtigsten OWASP-API-Risiken.
Diese Lektion in Kodokon öffnenOWASP pflegt eine Top 10 speziell für APIs. Die drei dominierenden Risiken: BOLA (Broken Object Level Authorization - auf das Objekt einer anderen Person zugreifen, indem man eine id ändert), Broken Authentication (schlecht geprüfte Tokens, schwache Secrets) und Unrestricted Resource Consumption (keine Grenze für Rate, Größe oder Dauer). Beachte, dass keines der drei durch die Installation einer Bibliothek behoben wird: Sie sind Eigenschaften deines Entwurfs. Beginnen wir mit den übergreifenden Mechanismen: CORS, Rate Limiting und Header.
import { createServer } from "node:http";
const allowed = new Set(["https://app.example.com"]);
const server = createServer((req, res) => {
const origin = req.headers.origin;
if (origin && allowed.has(origin)) {
res.setHeader(
"Access-Control-Allow-Origin",
origin,
);
res.setHeader("Vary", "Origin");
}
if (req.method === "OPTIONS") {
res.setHeader(
"Access-Control-Allow-Methods",
"GET,POST,PUT,DELETE",
);
res.setHeader(
"Access-Control-Allow-Headers",
"content-type,authorization",
);
res.setHeader("Access-Control-Max-Age", "86400");
res.writeHead(204).end();
return;
}
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ ok: true }));
});
server.listen(3000);Mach dir klar, was CORS nicht ist: ein Schutz für den Server. curl oder ein fremdes Backend ignorieren diese Header vollständig; nur der Browser setzt sie durch, um seine Nutzerinnen und Nutzer zu schützen. Drei Expertenregeln: Gib den exakten Origin aus einer Allowlist zurück (spiegle niemals blind den Header Origin), ergänze Vary: Origin, um Zwischen-Caches nicht zu vergiften, und wisse, dass der Platzhalter * von Browsern abgelehnt wird, sobald die Anfrage Cookies mitführt (credentials).
const WINDOW_MS = 60_000;
const LIMIT = 100;
const hits = new Map();
function check(ip) {
const now = Date.now();
const entry = hits.get(ip);
if (!entry || now - entry.start >= WINDOW_MS) {
hits.set(ip, { start: now, count: 1 });
return { ok: true, remaining: LIMIT - 1 };
}
entry.count += 1;
const remaining = Math.max(LIMIT - entry.count, 0);
return { ok: entry.count <= LIMIT, remaining };
}
console.log(check("203.0.113.7"));Security-Header kosten ein paar Zeilen und blockieren ganze Angriffsklassen: HSTS erzwingt HTTPS für künftige Besuche, X-Content-Type-Options: nosniff hindert den Browser daran, einen MIME-Typ zu erraten, und eine restriktive CSP (default-src 'none') macht die Ausnutzung einer versehentlich im Browser gerenderten API-Antwort unschädlich. Entferne außerdem jeden Header, der deinen Stack verrät: Express fügt standardmäßig X-Powered-By hinzu - schalte ihn mit app.disable('x-powered-by') ab.
function setSecurityHeaders(res) {
res.setHeader(
"Strict-Transport-Security",
"max-age=63072000; includeSubDomains",
);
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader(
"Content-Security-Policy",
"default-src 'none'; frame-ancestors 'none'",
);
res.setHeader("Cache-Control", "no-store");
}
export { setSecurityHeaders };