Die 10 kritischsten Sicherheitsrisiken von Webanwendungen und wie man sie entschärfen kann.
Die OWASP Top 10 ist das am weitesten verbreitete Dokument zur Sensibilisierung für die Sicherheit von Webanwendungen. Die Ausgabe 2025 spiegelt die aktuelle Bedrohungslandschaft wider und führt neue Kategorien wie Software Supply Chain Failures und Mishandling of Exceptional Conditions ein.
Die Zugriffskontrolle sorgt dafür, dass Benutzer nicht außerhalb der ihnen zugewiesenen Befugnisse handeln können. Fehler führen in der Regel zur unbefugten Offenlegung von Informationen, zur Änderung oder Zerstörung von Daten oder zur Ausführung einer Geschäftsfunktion außerhalb der vom Benutzer festgelegten Grenzen.
Angreifer können Schwachstellen in der Zugriffskontrolle ausnutzen, um auf die Konten anderer Benutzer zuzugreifen, sensible Dateien einzusehen, die Daten anderer Benutzer zu ändern oder Zugriffsrechte zu ändern.
# User ID taken directly from request without authorization check @app.route('/api/user/<user_id>/profile') def get_profile(user_id): user = db.get_user(user_id) # No ownership check! return jsonify(user.to_dict())
@app.route('/api/user/<user_id>/profile') @login_required def get_profile(user_id): # Verify the requesting user owns this resource if current_user.id != user_id and not current_user.is_admin: abort(403) user = db.get_user(user_id) if not user: abort(404) return jsonify(user.to_dict())
Eine falsche Sicherheitskonfiguration liegt vor, wenn die Sicherheitseinstellungen nicht korrekt definiert, implementiert oder gepflegt werden. Dazu gehören fehlende Sicherheitshärtung, unnötig aktivierte Funktionen, Standardkonten mit unveränderten Passwörtern, übermäßig ausführliche Fehlermeldungen und falsch konfigurierte HTTP-Sicherheits-Header.
Falsch konfigurierte Server, Frameworks oder Cloud-Dienste können sensible Daten preisgeben, unbefugten Zugriff ermöglichen oder Angreifern Informationen zur Planung weiterer Angriffe liefern.
# Debug mode enabled in production, verbose errors exposed app = Flask(__name__) app.config['DEBUG'] = True # Exposes stack traces! app.config['SECRET_KEY'] = 'default-secret' # Default key!
import os app = Flask(__name__) app.config['DEBUG'] = False app.config['SECRET_KEY'] = os.environ['SECRET_KEY'] @app.after_request def set_security_headers(response): response.headers['X-Content-Type-Options'] = 'nosniff' response.headers['X-Frame-Options'] = 'DENY' response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' response.headers['Content-Security-Policy'] = "default-src 'self'" return response
Neu im Jahr 2025. Konzentriert sich auf Risiken im Zusammenhang mit Komponenten Dritter, Abhängigkeiten und CI/CD-Pipelines. Angreifer greifen die Software-Lieferkette an, indem sie Pakete kompromittieren, bösartigen Code in Open-Source-Bibliotheken einschleusen oder Schwachstellen in der Build-Pipeline ausnutzen.
Eine einzige gefährdete Abhängigkeit kann Tausende von Anwendungen beeinträchtigen. Angriffe auf die Lieferkette können zu Datendiebstahl, zur Installation von Backdoors oder zur vollständigen Kompromittierung des Systems führen, ohne dass dies bemerkt wird.
// package.json with unpinned dependencies { "dependencies": { "express": "*", // Any version! "lodash": "^4.0.0", // Wide range "unknown-pkg": "^1.0.0" // Unvetted package } }
// package.json with pinned versions + lockfile + audit { "dependencies": { "express": "4.21.2", "lodash": "4.17.21" }, "scripts": { "preinstall": "npx npm-audit-resolver", "integrity-check": "npm audit signatures" } } // Also: use package-lock.json, enable Dependabot/Renovate, // generate SBOM, verify package provenance
Fehler im Zusammenhang mit der Kryptographie, die häufig zur Preisgabe sensibler Daten führen. Dazu gehören die Verwendung veralteter Algorithmen, eine schwache Schlüsselgenerierung, fehlende Verschlüsselung von Daten bei der Übertragung oder im Ruhezustand sowie eine unsachgemäße Zertifikatsvalidierung.
Eine schwache oder fehlende Verschlüsselung kann Passwörter, Kreditkartennummern, Gesundheitsdaten, persönliche Informationen und Geschäftsgeheimnisse offenlegen, was zu Verstößen gegen gesetzliche Vorschriften (GDPR, PCI DSS) führen kann.
import hashlib # Storing passwords with weak hashing def store_password(password: str) -> str: return hashlib.md5(password.encode()).hexdigest() # MD5 is broken!
import bcrypt def store_password(password: str) -> bytes: # Use bcrypt with automatic salting salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode(), salt) def verify_password(password: str, hashed: bytes) -> bool: return bcrypt.checkpw(password.encode(), hashed)
Injection-Fehler treten auf, wenn nicht vertrauenswürdige Daten als Teil eines Befehls oder einer Abfrage an einen Interpreter gesendet werden. SQL-Injektion, NoSQL-Injektion, OS-Befehlsinjektion und Cross-Site Scripting (XSS) sind die häufigsten Formen. Feindliche Daten können den Interpreter dazu verleiten, unbeabsichtigte Befehle auszuführen.
Eine Injektion kann zu Datenverlust, Beschädigung, unbefugtem Zugriff, vollständiger Hostübernahme oder Denial-of-Service führen. Allein SQL-Injection ist nach wie vor einer der gefährlichsten und am weitesten verbreiteten Angriffsvektoren.
# SQL injection via string concatenation def get_user(username: str): query = f"SELECT * FROM users WHERE name = '{username}'" return db.execute(query) # username = "' OR '1'='1"
# Parameterized queries prevent SQL injection def get_user(username: str): query = "SELECT * FROM users WHERE name = %s" return db.execute(query, (username,)) # For XSS prevention, escape output from markupsafe import escape def render_comment(comment: str) -> str: return f"<p>{escape(comment)}</p>"
Unsicheres Design bezieht sich auf Risiken, die mit Design- und Architekturfehlern zusammenhängen. Sie erfordern die Verwendung von Bedrohungsmodellen, sicheren Entwurfsmustern und Referenzarchitekturen. Ein unsicherer Entwurf kann nicht durch eine perfekte Implementierung behoben werden; die erforderlichen Sicherheitskontrollen wurden nie zur Abwehr bestimmter Angriffe geschaffen.
Konstruktionsmängel können zu Schwachstellen in der Geschäftslogik führen, die mit automatisierten Tools nur schwer zu erkennen sind. Fehlende Ratenbegrenzungen für sensible Vorgänge, fehlende Multi-Faktor-Authentifizierung für kritische Aktionen oder unzureichende Betrugserkennung sind allesamt Designfehler.
# Password reset with no rate limit or verification @app.route('/reset-password', methods=['POST']) def reset_password(): email = request.form['email'] new_pass = request.form['new_password'] user = db.find_by_email(email) user.password = hash_password(new_pass) # No token verification! db.save(user)
from datetime import datetime, timedelta @app.route('/reset-password', methods=['POST']) @rate_limit("3/hour") def reset_password(): token = request.form['token'] new_pass = request.form['new_password'] # Verify time-limited, single-use token reset_req = db.find_reset_token(token) if not reset_req or reset_req.used or reset_req.expires < datetime.utcnow(): abort(400, "Invalid or expired token") # Enforce password complexity if not meets_password_policy(new_pass): abort(400, "Password does not meet requirements") reset_req.user.password = hash_password(new_pass) reset_req.used = True db.save_all([reset_req.user, reset_req])
Die Bestätigung der Identität eines Benutzers, die Authentifizierung und die Sitzungsverwaltung sind entscheidend. Authentifizierungsfehler treten auf, wenn Anwendungen Credential Stuffing, Brute-Force, schwache Passwörter oder eine mangelhafte Sitzungsverwaltung zulassen, z. B. durch nicht rotierende Sitzungs-IDs nach der Anmeldung.
Angreifer können sich durch automatisches Ausfüllen von Anmeldeinformationen, Brute-Force-Verfahren oder Session-Hijacking Zugang zu Benutzerkonten verschaffen. Kompromittierte Konten können zu Identitätsdiebstahl, Betrug und Datenschutzverletzungen führen.
# No brute force protection, weak session handling @app.route('/login', methods=['POST']) def login(): user = db.find_by_email(request.form['email']) if user and user.password == request.form['password']: # Plain comparison! session['user'] = user.id # Session ID not rotated return redirect('/dashboard')
from flask_limiter import Limiter limiter = Limiter(app, default_limits=["100/hour"]) @app.route('/login', methods=['POST']) @limiter.limit("5/minute") def login(): user = db.find_by_email(request.form['email']) if not user or not bcrypt.checkpw( request.form['password'].encode(), user.password_hash ): # Generic error to prevent user enumeration return "Invalid credentials", 401 # Rotate session ID after authentication session.regenerate() session['user'] = user.id # Check for MFA requirement if user.mfa_enabled: return redirect('/mfa-verify') return redirect('/dashboard')
Software- und Datenintegritätsfehler beziehen sich auf Code und Infrastruktur, die nicht vor Integritätsverletzungen schützen. Dazu gehören unsichere Deserialisierung, die Verwendung von nicht vertrauenswürdigen CDNs oder Plugins ohne Integritätsprüfung und automatische Aktualisierungsmechanismen ohne signierte Updates.
Unsichere Deserialisierung kann zu Remotecodeausführung führen. Das Laden von Skripten aus nicht vertrauenswürdigen CDNs ohne Subresource Integrity (SRI) kann es Angreifern ermöglichen, bösartigen Code in Ihre Anwendung zu injizieren.
<!-- Loading external scripts without integrity checks --> <script src="https://cdn.example.com/lib.js"></script> <!-- Insecure deserialization in Python --> import pickle data = pickle.loads(user_input) # Arbitrary code execution!
<!-- Use Subresource Integrity (SRI) for external scripts --> <script src="https://cdn.example.com/lib.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K..." crossorigin="anonymous"></script> # Use safe deserialization in Python import json data = json.loads(user_input) # Safe: only parses JSON data
Ohne ausreichende Protokollierung, Überwachung und Alarmierung können Sicherheitsverletzungen nicht rechtzeitig erkannt werden. Unzureichende Protokollierung, unzureichende Integration mit Systemen zur Reaktion auf Vorfälle und fehlende Echtzeit-Warnungen ermöglichen es Angreifern, weitere Angriffe auf Systeme zu verüben, diese aufrechtzuerhalten und Daten zu manipulieren oder zu extrahieren.
Ohne ordnungsgemäße Protokollierung können Angreifer über längere Zeiträume unentdeckt bleiben. Die meisten Studien über Sicherheitsverletzungen zeigen, dass es im Durchschnitt mehr als 200 Tage dauert, bis eine Sicherheitsverletzung entdeckt wird, wobei diese oft von externen Parteien und nicht durch interne Überwachung entdeckt wird.
# No logging of security-relevant events @app.route('/login', methods=['POST']) def login(): user = authenticate(request.form) if not user: return "Login failed", 401 # No record of failure return redirect('/dashboard')
import logging from datetime import datetime security_log = logging.getLogger('security') @app.route('/login', methods=['POST']) def login(): user = authenticate(request.form) if not user: security_log.warning( "LOGIN_FAILED | ip=%s | email=%s | time=%s", request.remote_addr, request.form.get('email', 'unknown'), datetime.utcnow().isoformat(), ) check_brute_force(request.remote_addr) return "Invalid credentials", 401 security_log.info( "LOGIN_SUCCESS | user_id=%s | ip=%s", user.id, request.remote_addr, ) return redirect('/dashboard')
Neu im Jahr 2025. Anwendungen, die Fehler, Ausnahmen und Randfälle unsachgemäß behandeln, können vertrauliche Informationen preisgeben, in inkonsistente Zustände geraten oder ausnutzbare Bedingungen schaffen. Dazu gehören ausführliche Fehlermeldungen in der Produktion, nicht abgefangene Ausnahmen, die Sicherheitskontrollen umgehen, und Race Conditions.
Durch unsachgemäße Fehlerbehandlung können Stack Traces, Datenbankdetails oder interne Pfade aufgedeckt werden. Race Conditions in Sicherheitsprüfungen können Time-of-Check-to-Time-of-Use (TOCTOU)-Angriffe ermöglichen, bei denen die Autorisierung oder Zahlungsvalidierung umgangen wird.
# Exposing internal details in error messages @app.route('/api/data') def get_data(): try: result = db.query(request.args['q']) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 # Leaks DB details!
import uuid, logging logger = logging.getLogger(__name__) @app.errorhandler(Exception) def handle_exception(e): error_id = str(uuid.uuid4()) logger.error("Unhandled exception [%s]: %s", error_id, e, exc_info=True) # Return generic error with reference ID for support return jsonify({ "error": "An internal error occurred", "reference": error_id, }), 500 @app.route('/api/data') def get_data(): q = request.args.get('q') if not q or not is_valid_query(q): return jsonify({"error": "Invalid query parameter"}), 400 result = db.query(q) return jsonify(result)
| ID | Schwachstelle | Schweregrad | Schlüssel Abschwächung |
|---|---|---|---|
| A01 | Defekte Zugangskontrolle | Critical | Serverseitige Zugriffsprüfungen, standardmäßig verweigern, Eigentumsverhältnisse aufzeichnen |
| A02 | Sicherheit Fehlkonfiguration | High | Härtungsprozess, Sicherheits-Header, Standardeinstellungen deaktivieren |
| A03 | Misserfolge in der Software-Lieferkette | High | Angepinnte Abhängigkeiten, SBOM, Scannen von Abhängigkeiten |
| A04 | Kryptographische Ausfälle | High | Starke Algorithmen, TLS 1.2+, Schlüsselverwaltung |
| A05 | Einspritzung | Critical | Parametrisierte Abfragen, Ausgabekodierung, Eingabevalidierung |
| A06 | Unsicheres Design | High | Modellierung von Bedrohungen, sichere Muster, Testen von Missbrauchsfällen |
| A07 | Fehler bei der Authentifizierung | High | MFA, Brute-Force-Schutz, Sitzungsrotation |
| A08 | Fehler in der Software oder Datenintegrität | High | SRI, signierte Aktualisierungen, sichere Deserialisierung |
| A09 | Fehler bei der Sicherheitsprotokollierung und Alarmierung | Medium | Zentralisierte Protokollierung, Echtzeit-Warnungen, Reaktion auf Vorfälle |
| A10 | Falscher Umgang mit außergewöhnlichen Bedingungen | Medium | Globale Fehlerbehandlung, generische Meldungen, TOCTOU-Verhinderung |