Warum Sicherheit die Aufgabe aller ist
Web-Sicherheit ist kein Spezialgebiet, das von jemand anderem erledigt wird. Jedes Formular, jeder gerenderte Kommentar, jedes Cookie und jeder API-Endpunkt bietet die Chance für einen Fehler, der Ihre Nutzer gefährdet. Die Kosten eines Sicherheitsfehlers sind ungewöhnlich hoch: Datenverlust, zerstörtes Vertrauen und rechtliche Konsequenzen.
Die gute Nachricht ist, dass die meisten Angriffe nur wenigen Mustern folgen und die meisten Abwehrmaßnahmen gut verstanden sind. Dieser Guide deckt die Grundlagen ab: Cross-Site Scripting, Cross-Site Request Forgery, CORS, sichere Authentifizierung und die Browser-Header, mit denen Sie Ihre Regeln durchsetzen.
Die goldene Regel
Vertraue niemals dem Client. Alles, was vom Browser kommt, kann gefälscht sein: Formularwerte, Header, versteckte Felder, Preise, User-IDs und der JavaScript-State. Clientseitige Validierung dient der User Experience; der Server muss alles erneut validieren und autorisieren.
Dieses eine Prinzip ist die Grundlage fast jeder Sicherheitsmaßnahme. Wenn du davon ausgehst, dass Inputs bösartig sind und Berechtigungen serverseitig geprüft werden müssen, verschwinden die meisten Schwachstellen, noch bevor sie programmiert werden.
Cross-site scripting (XSS)
XSS ist die am häufigsten vorkommende schwerwiegende Web-Schwachstelle. Sie tritt auf, wenn vom Angreifer kontrollierte Daten als Code behandelt werden. Es gibt drei Hauptarten: stored (auf dem Server gespeichert und an andere ausgeliefert), reflected (in einer Antwort zurückgegeben) und DOM-based (durch clientseitiges JavaScript eingeführt).
Die primäre Verteidigungsmaßnahme ist das Output Encoding: Kodieren Sie Daten für den Kontext, in dem sie erscheinen. Verwenden Sie im DOM textContent anstelle von innerHTML.
// safe.js
const el = document.createElement("p");
el.textContent = userComment; // escaped
// If you truly need HTML, sanitise it
import DOMPurify from "dompurify";
el.innerHTML = DOMPurify.sanitize(userHtml);
Verwenden Sie auf dem Server Templating, das standardmäßig escaped, und erstellen Sie SQL oder HTML niemals durch String-Konkatenation. Verwenden Sie für Datenbanken immer parametrisierte Queries oder ein ORM, damit Eingaben niemals zu SQL werden können.
Content Security Policy
Eine Content Security Policy ist ein Response-Header, der dem Browser mitteilt, welche Quellen eine Seite verwenden darf. Eine strikte Policy verwandelt viele XSS-Bugs in harmlose Konsolenfehler.
Content-Security-Policy:
default-src 'self';
script-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Beginnen Sie mit Content-Security-Policy-Report-Only, um Verstöße zu erfassen, ohne die Seite funktionsunfähig zu machen, und stellen Sie die Policy erst danach verbindlich ein. Vermeiden Sie unsafe-inline und unsafe-eval, wo immer es möglich ist, und verwenden Sie Nonces oder Hashes für Inline-Scripts, die Sie nicht entfernen können.
Cross-site request forgery (CSRF)
CSRF täuscht den Browser dazu, eine authentifizierte Anfrage unter Verwendung der Cookies des Opfers zu senden. Wenn Ihre Seite cookie-basierte Sessions nutzt und ein zustandsändernder Endpoint einfache Anfragen akzeptiert, kann eine Seite eines Angreifers diese auslösen.
Abwehrmaßnahmen, in Schichten:
- SameSite cookies.
SameSite=LaxoderStrictverhindert in modernen Browsern, dass Cookies bei Cross-Site-Anfragen gesendet werden. - Anti-CSRF tokens. Ein pro Session generierter Token, der in Formularen enthalten und auf dem Server verifiziert wird.
- Origin prüfen. Lehnen Sie zustandsändernde Anfragen ab, deren
OriginHeader nicht Ihre eigene Seite ist. - Niemals Mutationen via GET. Verwenden Sie POST, PUT, PATCH oder DELETE für Änderungen.
CORS
CORS ist eine Browser-Regel, die festlegt, welche Origins Antworten Ihrer API lesen dürfen. Standardmäßig kann eine Seite keine Cross-Origin-Antwort lesen, es sei denn, der Server erlaubt dies über Access-Control-Allow-Origin.
Zwei Dinge werden häufig missverstanden:
- CORS schützt den Browser des Nutzers, nicht Ihren Server. Andere Server können Ihre API ungehindert aufrufen; nur eine serverseitige Autorisierung kann dies verhindern.
- Ein permissives
Access-Control-Allow-Origin: *in Kombination mit Credentials ist nicht zulässig, und das Spiegeln beliebiger Origins ist gefährlich.
Konfigurieren Sie CORS explizit für die Origins, denen Sie vertrauen, und behalten Sie die Authentifizierung und Autorisierung auf dem Server.
Sichere Authentifizierung
Bei der Authentifizierung sind Fehler am kostspieligsten.
- Passwörter hashen mit bcrypt, scrypt oder Argon2; niemals im Klartext oder mittels reversibler Verschlüsselung speichern.
- HttpOnly, Secure und SameSite Cookies für Sessions verwenden, damit JavaScript den Token nicht auslesen kann.
- Kurzlebige Tokens mit Refresh-Mechanismus bevorzugen und diese rotieren.
- Multi-Faktor-Authentifizierung für sensible Accounts implementieren.
- Login-Versuche rate-limitieren und nach wiederholten Fehlversuchen sperren.
- Geheimnisse niemals im Client-Code hinterlegen — alles, was an den Browser gesendet wird, ist öffentlich.
Der obige Vergleich zeigt den Trade-off zwischen Cookies und localStorage. localStorage Tokens sind zwar praktisch, aber durch jedes Skript lesbar, sodass ein einziger XSS-Angriff die Session stehlen kann. HttpOnly Cookies schließen diesen Weg aus.
Weitere Essentials
- Überall HTTPS, inklusive HSTS, um Downgrade-Angriffe zu verhindern.
- Input-Validierung und Encoding auf dem Server für jedes einzelne Feld.
- Prinzip der geringsten Berechtigung (Least Privilege) für Datenbank-Benutzer, API keys und Cloud-Rollen anwenden.
- Security Header setzen: CSP, HSTS,
X-Content-Type-Options: nosniff,Referrer-Policyundframe-ancestors. - Dependencies aktuell halten und regelmäßig auditieren.
- Keine Details preisgeben in Fehlermeldungen oder Stack Traces in der Production-Umgebung.
- Authentifizierungs-Events und Berechtigungsfehler loggen und überwachen.
Best Practices
- Behandle alle Client-Inputs als nicht vertrauenswürdig und validiere sie auf dem Server.
- Kodiere die Ausgabe entsprechend ihrem Kontext; verwende
textContentund bereinige jegliches HTML. - Nutze für jeden Datenbankzugriff parametrisierte Queries.
- Speichere Sessions in HttpOnly, Secure und SameSite Cookies.
- Implementiere eine Content Security Policy und starte zunächst im Report-Only-Modus.
- Verwende SameSite zusammen mit Anti-CSRF-Tokens für zustandsändernde Requests.
- Bewahre Secrets auf dem Server auf und rotiere diese regelmäßig.
Häufige Fehler
- Verwendung von
innerHTMLmit Benutzerdaten. - Speichern von Session-Tokens in
localStorage. - Vertrauen auf clientseitige Validierung oder versteckte Formularfelder.
- Erstellung von SQL-Abfragen mittels String-Konkatenation.
- Die Annahme, dass CORS ein Mechanismus zur Zugriffskontrolle ist.
- Ausliefern von API-Keys im Front-End-Code.
Wie geht es weiter?
Sicherheit ist ein fortlaufender Prozess und keine Checkliste, die man einfach abhakt. Verankern Sie Ihre Sicherheitsmaßnahmen im HTTP-Protokoll, setzen Sie diese auf dem Node.js-Server durch und gehen Sie sicher mit Secrets um, wenn Sie Ihre Anwendung mit Docker bereitstellen. Lesen Sie die OWASP Top 10 und prüfen Sie anschließend ein Formular in Ihrer eigenen App von Anfang bis Ende – fast immer wird man etwas finden, das es zu verbessern gilt.