OWASP Top 10: Die häufigsten Sicherheitslücken im Web
OWASP Top 10 ist die Liste der häufigsten Webanwendungs-Schwachstellen, herausgegeben vom Open Web Application Security Project. Sie wird alle paar Jahre überarbeitet — die aktuelle Version ist von 2021, eine neue für 2025/26 ist angekündigt. Die Reihenfolge ändert sich, die Themen weniger.
Ich gehe hier nicht jeden Punkt durch, sondern picke die Stellen heraus, die in meiner Erfahrung am häufigsten in Production-Code falsch gemacht werden — und die mit überschaubarem Aufwand zu beheben wären.
Broken Access Control
Steht seit 2021 auf Platz 1 und wird vermutlich da bleiben. Klassiker: ein User kann via URL-Manipulation auf Daten zugreifen, die ihm nicht gehören.
// Falsch
app.get('/orders/:id', (req, res) => {
const order = db.orders.findById(req.params.id);
res.json(order);
});
// Richtig
app.get('/orders/:id', requireAuth, (req, res) => {
const order = db.orders.findById(req.params.id);
if (order.userId !== req.user.id) return res.status(403).end();
res.json(order);
});
Klingt offensichtlich, ist es nicht. Ich habe in produktivem Code mehrfach Endpoints gefunden, die genau so aussahen wie das erste Beispiel — manchmal von erfahrenen Devs, weil "der Frontend macht ja schon die Filterung". Das Frontend macht keine Sicherheit.
Cryptographic Failures
Hauptpunkt: Daten unverschlüsselt übertragen oder mit unsicheren Algorithmen gespeichert.
In der Praxis 2026 kein großes Problem mehr für Transport (HTTPS überall), aber für Storage:
- Passwörter mit MD5 oder SHA-1 hashen — bitte nicht
- Standard ist
bcrypt,argon2idoderscrypt - Tokens als Klartext in der DB speichern statt sie zu hashen
- API-Keys im Code statt im Secret Manager
Wer Userdaten verwaltet, sollte einmal bcrypt.gensalt() und bcrypt.checkpw() lernen — sind drei Zeilen, hält ewig.
Injection
SQL-Injection ist weniger geworden, weil ORMs Standard sind. Dafür mehr NoSQL-, LDAP- und OS-Command-Injection.
Generische Regel: niemals User-Input in einen Befehl konkatenieren, immer parametrisieren.
# Falsch
db.execute(f"SELECT * FROM users WHERE name = '{name}'")
# Richtig
db.execute("SELECT * FROM users WHERE name = %s", (name,))
Der Unterschied ist nicht Geschmack, sondern Sicherheit. Mit Parametrisierung weiß die DB, dass name ein Wert ist, kein Code.
Security Misconfiguration
Der vermutlich häufigste Ist-Zustand. Default-Konfigurationen, die nie angepasst wurden. Debug-Endpoints in Production. CORS-Header, die * erlauben. Stack-Traces im Frontend.
Konkrete Quick-Wins für nginx:
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
CSP ist die wichtigste — und die, die am meisten Aufwand braucht. Eine CSP, die wirklich greift, ist nicht "default-src 'self'" als One-Liner, sondern eine sorgfältige Liste von erlaubten Quellen für Scripts, Styles, Fonts, etc.
Vulnerable and Outdated Components
Dependency-Updates. Wer npm audit oder pip-audit nicht regelmäßig laufen lässt, sammelt Schwachstellen. GitHub Dependabot macht das automatisch — als Public Repo gratis, als Privat-Repo mit Pro-Account.
Mein Pattern: Dependabot-PRs werden wöchentlich gesammelt durchgesehen, nicht einzeln. Sonst wird jeder Push zur Routine-Bestätigung ohne Aufmerksamkeit.
Identification and Authentication Failures
Das umfasst zu viel, um es kurz zu fassen. Die wichtigsten Punkte:
- Brute-Force-Schutz auf Login-Endpoints (Rate Limiting)
- Multi-Faktor-Authentifizierung anbieten, idealerweise erzwingen
- Sessions invalidieren bei Passwort-Wechsel
- Passwortregeln, die nicht "8 Zeichen, 1 Zahl, 1 Sonderzeichen" sind, sondern Mindestlänge 12 + Check gegen bekannte gehackte Passwörter (
zxcvbn,pwned-passwords)
Server-Side Request Forgery
Ein User-kontrollierter URL-Parameter wird vom Server aus aufgerufen. Klassischer Angriffspunkt: Profile-Bild-Uploads per URL, Webhook-URLs, "fetch URL"-Funktionen.
# Falsch
def fetch_image(url):
return requests.get(url).content
# Richtig
ALLOWED = ['https://cdn.example.com', 'https://images.example.com']
def fetch_image(url):
if not any(url.startswith(p) for p in ALLOWED):
raise ValueError("Disallowed URL")
return requests.get(url, timeout=5, allow_redirects=False).content
Auf Cloud-Servern besonders kritisch, weil interne Metadata-Endpoints (z.B. 169.254.169.254 bei AWS) sonst Credentials leaken können.
Was OWASP nicht explizit so listet, aber sollte
Logging und Monitoring. OWASP hat das in den Top 10, aber als reine "do log security events". In der Praxis: ohne strukturierte Logs und einen Mechanismus, der bei Anomalien alarmiert, sind die anderen 9 Punkte halb nutzlos. Ein Angriff, den niemand bemerkt, ist ein erfolgreicher Angriff.
Threat Modeling vor jedem Feature. Nicht formell — nur die Frage: "Was kann hier schiefgehen, und was passiert dann?" Fünf Minuten beim Designen sparen Wochen beim Reagieren.
Sicherheit ist ein Spektrum, nicht binär. OWASP-Compliance ist ein guter Ausgangspunkt, kein Endzustand.