Security-Audit
OWASP Top 10: Die häufigsten Web-Schwachstellen verstehen und verhindern
Die OWASP Top 10 sind seit Jahren der De-facto-Standard für Web-Security. Aber zwischen „ich kenne die Liste" und „meine Architektur verhindert diese Schwachstellen" liegen Welten. Dieser Artikel erklärt jede Kategorie aus Architektur-Perspektive: Was passiert technisch, wie entstehen diese Schwachstellen, und was können Sie strukturell dagegen tun.
Security ist kein Feature, das Sie am Ende hinzufügen. Security ist eine Architektur-Entscheidung.
Überblick: Die OWASP Top 10
Top 10:2025 (Release Candidate)
Am 6. November 2025 hat OWASP die Top 10:2025 als Release Candidate (RC) veröffentlicht. Die Liste ist inhaltlich sehr aussagekräftig, kann sich aber bis zur finalen Version noch in Details ändern:
| # | Kategorie | Kurzbeschreibung |
|---|---|---|
| A01 | Broken Access Control | Benutzer können auf Daten/Funktionen zugreifen, die ihnen nicht gehören |
| A02 | Security Misconfiguration | Unsichere Default-Einstellungen, offene Cloud-Buckets, Debug-Modi |
| A03 | Software Supply Chain Failures | Kompromittierte Dependencies, unsichere Build-Pipelines, fehlende SBOM |
| A04 | Cryptographic Failures | Sensitive Daten unverschlüsselt oder mit schwacher Krypto |
| A05 | Injection | SQL, NoSQL, OS, LDAP Injection durch unvalidierte Eingaben |
| A06 | Insecure Design | Architektonische Schwächen, die nicht durch Code-Fixes behebbar sind |
| A07 | Authentication Failures | Schwache Passwörter, fehlende MFA, Session-Management-Fehler |
| A08 | Software or Data Integrity Failures | Unsichere Deserialisierung, fehlende Signaturprüfung |
| A09 | Security Logging and Alerting Failures | Fehlende Logs, kein Alerting bei Security-Events |
| A10 | Mishandling of Exceptional Conditions | Fehlerhafte Exception-Behandlung, Race Conditions, Resource Exhaustion |
Hinweis: Die Reihenfolge spiegelt Häufigkeit und Impact wider, nicht zwingend das individuelle Risiko einer Anwendung. Kontext schlägt Ranking.
Top 10:2021 (aktuell offiziell)
Die zuletzt offiziell veröffentlichte („released") Version bleibt Top 10:2021. Sie ist weiterhin die Referenz für Audits und Compliance:
| # | Kategorie (2021) | Status in 2025 |
|---|---|---|
| A01 | Broken Access Control | → A01 (bleibt #1) |
| A02 | Cryptographic Failures | → A04 (abgestuft) |
| A03 | Injection | → A05 (abgestuft) |
| A04 | Insecure Design | → A06 (abgestuft) |
| A05 | Security Misconfiguration | → A02 (hochgestuft) |
| A06 | Vulnerable and Outdated Components | → A03 Supply Chain (erweitert) |
| A07 | Identification and Authentication Failures | → A07 (bleibt) |
| A08 | Software and Data Integrity Failures | → A08 (bleibt) |
| A09 | Security Logging and Monitoring Failures | → A09 + Alerting (erweitert) |
| A10 | Server-Side Request Forgery (SSRF) | → In A01 integriert |
- Neu A03: Software Supply Chain Failures – Log4Shell und SolarWinds haben diese Kategorie geprägt
- Neu A10: Mishandling of Exceptional Conditions – Exception-Handling, Race Conditions, Resource Exhaustion
- SSRF raus: Nicht mehr als eigene Kategorie, aber weiterhin relevant (oft unter A01 oder A06)
A01Broken Access Control
Die #1 der OWASP Top 10 – und das aus gutem Grund. Broken Access Control bedeutet: Ein Benutzer kann auf Ressourcen zugreifen, für die er keine Berechtigung hat.
Wie es passiert
// Typischer Fehler: IDOR (Insecure Direct Object Reference)
@GetMapping("/api/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
// FALSCH: Keine Prüfung, ob der User diese Order sehen darf
return orderRepository.findById(orderId);
}
// Richtig: Prüfung gegen den authentifizierten User
@GetMapping("/api/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId, Authentication auth) {
Order order = orderRepository.findById(orderId);
if (!order.getUserId().equals(auth.getUserId())) {
throw new AccessDeniedException("Not your order");
}
return order;
}
Weitere Ausprägungen
- Privilege Escalation: Normaler User erreicht Admin-Funktionen
- Path Traversal:
/api/files?name=../../../etc/passwd - Forced Browsing: Erraten von URLs zu geschützten Ressourcen
- CORS Misconfiguration: Zu offene Cross-Origin-Policies
Architektonische Gegenmaßnahmen
- Default Deny: Alles ist verboten, bis es explizit erlaubt wird
- Zentrale Autorisierung: Nicht in jedem Controller, sondern in einem Security-Layer
- Ownership-Checks: Jede Ressource hat einen Owner, der geprüft wird
- Rate Limiting: Erschwert Enumeration-Angriffe
// Spring Security: Zentrale Method-Security
@PreAuthorize("@orderSecurity.isOwner(#orderId, authentication)")
@GetMapping("/api/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
return orderRepository.findById(orderId);
}
// Security-Service
@Component("orderSecurity")
public class OrderSecurityService {
public boolean isOwner(Long orderId, Authentication auth) {
Order order = orderRepository.findById(orderId);
return order.getUserId().equals(auth.getUserId());
}
}
A02Security Misconfiguration
Von A05 auf A02 hochgestuft – Security Misconfiguration ist die am einfachsten zu vermeidende und trotzdem häufigste Kategorie. Unsichere Defaults, vergessene Debug-Modi, offene Cloud-Buckets.
Typische Fehler
- Default Credentials: admin/admin, root/root
- Verbose Error Messages: Stack Traces an den Client
- Unnötige Features: phpMyAdmin in Production
- Fehlende Security Headers: CSP, X-Frame-Options, etc.
- Offene Directory Listings
- Cloud Misconfig: Öffentlich lesbare S3 Buckets
# Nginx: Sichere Headers setzen
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self';" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# X-XSS-Protection ist deprecated – CSP ist die relevante Maßnahme
# Server-Version verstecken
server_tokens off;
# Spring Boot: Sichere Defaults
# application-prod.yml
server:
error:
include-message: never
include-stacktrace: never
include-binding-errors: never
management:
endpoints:
web:
exposure:
include: health # Nur health, nicht alles
Architektonische Gegenmaßnahmen
- Infrastructure as Code: Terraform, Pulumi – reproduzierbar, reviewbar
- Hardened Base Images: Keine Defaults übernehmen
- Security Scanning: Cloud Security Posture Management (CSPM)
- Minimal Footprint: Nur installieren, was nötig ist
- Environment Parity: Prod-ähnliche Staging-Umgebung
A03Software Supply Chain Failures
Neu in 2025. Log4Shell (CVE-2021-44228) und der SolarWinds-Angriff haben diese Kategorie geprägt. Sie erweitert „Vulnerable Components" um die gesamte Software-Lieferkette.
Was dazugehört
- Vulnerable Dependencies: Veraltete Libraries mit bekannten CVEs
- Malicious Packages: Typosquatting, Dependency Confusion
- Kompromittierte Build-Pipelines: CI/CD als Angriffsvektor
- Fehlende SBOM: Keine Übersicht über verwendete Komponenten
- Unsigned Artifacts: Keine Integritätsprüfung
# Ihre App hat 20 direkte Dependencies
# Jede davon hat transitive Dependencies
# Am Ende: 200+ Libraries in Ihrem Build
$ npm ls --all | wc -l
847
$ mvn dependency:tree | wc -l
312
# Kennen Sie alle? Wissen Sie, welche Versionen sicher sind?
Tools zur Supply Chain Security
| Tool | Fokus | Typ |
|---|---|---|
| OWASP Dependency-Check | CVE-Scan für Dependencies | Open Source |
| Snyk | Dependencies + Container | SaaS (Free Tier) |
| Trivy | Container, IaC, SBOM | Open Source |
| Sigstore/Cosign | Artifact Signing | Open Source |
| SLSA Framework | Build Provenance | Standard |
# SBOM generieren (Software Bill of Materials)
$ syft packages myapp:latest -o spdx-json > sbom.json
# SBOM auf Vulnerabilities prüfen
$ grype sbom:sbom.json
# Container-Image signieren
$ cosign sign --key cosign.key myregistry/myapp:latest
# Signatur verifizieren
$ cosign verify --key cosign.pub myregistry/myapp:latest
Architektonische Gegenmaßnahmen
- SBOM: Wissen, was in Ihrem Build ist
- Artifact Signing: Nur signierte Packages deployen
- Private Registry: Nexus, Artifactory als Proxy
- Dependency Pinning: Lock-Files mit Hashes
- CI/CD Hardening: Least Privilege, Audit Logs
A04Cryptographic Failures
Früher „Sensitive Data Exposure" – umbenannt, weil die Ursache meist in fehlender oder schlechter Kryptografie liegt.
Typische Fehler
- Passwörter im Klartext oder mit MD5/SHA1 (ohne Salt)
- HTTP statt HTTPS für sensible Daten
- Veraltete Protokolle: TLS 1.0/1.1, SSLv3
- Hardcoded Keys: Encryption Keys im Code oder Config
- Fehlende Encryption at Rest: Datenbank unverschlüsselt
// FALSCH: MD5 für Passwörter
String hashedPassword = DigestUtils.md5Hex(password);
// FALSCH: SHA256 ohne Salt
String hashedPassword = DigestUtils.sha256Hex(password);
// RICHTIG: BCrypt mit Work Factor
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt(12));
// RICHTIG: Argon2 (aktueller Standard)
// Parameter abhängig von Hardware und Latenzanforderungen wählen
Argon2PasswordEncoder encoder = new Argon2PasswordEncoder(16, 32, 1, 65536, 3);
String hashedPassword = encoder.encode(password);
Architektonische Gegenmaßnahmen
- TLS überall: Auch interne Kommunikation (Zero Trust)
- Secrets Management: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault
- Data Classification: Wissen, welche Daten sensibel sind
- Encryption at Rest: Datenbank, Backups, Logs
- Key Rotation: Automatisiert, nicht manuell
A05Injection
Der Klassiker. Injection passiert, wenn User-Input als Code interpretiert wird – SQL, NoSQL, OS Commands, LDAP, XPath.
SQL Injection im Detail
// FALSCH: String Concatenation
String query = "SELECT * FROM users WHERE username = '" + username + "'";
// Input: admin'--
// Ergebnis: SELECT * FROM users WHERE username = 'admin'--'
// Das -- kommentiert den Rest aus → Login als admin ohne Passwort
// RICHTIG: Prepared Statements
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE username = ?"
);
stmt.setString(1, username);
// RICHTIG: ORM (JPA/Hibernate)
@Query("SELECT u FROM User u WHERE u.username = :username")
User findByUsername(@Param("username") String username);
Andere Injection-Varianten
// NoSQL Injection (MongoDB)
// FALSCH
db.users.find({ username: req.body.username, password: req.body.password })
// Input: { "username": "admin", "password": { "$ne": "" } }
// Ergebnis: Findet admin, wenn Passwort nicht leer ist (immer wahr)
// OS Command Injection
// FALSCH
String cmd = "ping " + userInput;
Runtime.getRuntime().exec(cmd);
// Input: localhost; rm -rf /
// Ergebnis: Ping + Löschung des Dateisystems
// LDAP Injection
// FALSCH
String filter = "(uid=" + username + ")";
// Input: *)(uid=*))(|(uid=*
// Ergebnis: Filter wird manipuliert
Architektonische Gegenmaßnahmen
- Parameterized Queries: Immer. Überall. Keine Ausnahmen.
- ORM verwenden: Abstrahiert SQL, verhindert die meisten Injections
- Input Validation: Whitelist, nicht Blacklist
- Least Privilege: DB-User hat nur nötige Rechte
- WAF: Web Application Firewall als zusätzliche Schicht
A06Insecure Design
Neu seit 2021. Diese Kategorie adressiert Schwachstellen, die nicht durch besseren Code gefixt werden können – weil das Design selbst unsicher ist.
Beispiele
- Fehlende Rate Limits: Brute-Force auf Login möglich
- Vorhersagbare Reset-Tokens: Passwort-Reset mit sequentiellen IDs
- Fehlende Business-Logik-Prüfungen: Negativer Warenkorb-Betrag möglich
- Keine Separierung: Alle Services im selben Netzwerk
// Insecure Design: Vorhersagbare Reset-Tokens
// FALSCH
String resetToken = String.valueOf(System.currentTimeMillis());
// Angreifer kann Token erraten: 1701432000000, 1701432000001, ...
// RICHTIG: Kryptographisch sichere Tokens
String resetToken = UUID.randomUUID().toString();
// Oder besser: SecureRandom
byte[] tokenBytes = new byte[32];
new SecureRandom().nextBytes(tokenBytes);
String resetToken = Base64.getUrlEncoder().encodeToString(tokenBytes);
Architektonische Gegenmaßnahmen
- Threat Modeling: Vor dem Coding, nicht danach
- Security Requirements: Explizit in User Stories
- Abuse Cases: Was kann schiefgehen?
- Defense in Depth: Mehrere Sicherheitsschichten
A07Identification and Authentication Failures
Schwache Authentifizierung ist ein Klassiker. Trotz aller Warnungen: Passwörter wie „123456" funktionieren immer noch in zu vielen Systemen.
Typische Schwachstellen
- Keine Brute-Force-Protection: Unbegrenzte Login-Versuche
- Schwache Passwort-Policies: Minimum 4 Zeichen, keine Komplexität
- Credential Stuffing: Keine Erkennung von automatisierten Angriffen
- Session Fixation: Session-ID bleibt nach Login gleich
- Fehlende MFA: Nur Passwort, kein zweiter Faktor
// Session Fixation verhindern
@PostMapping("/login")
public String login(HttpServletRequest request, ...) {
// Alte Session invalidieren
request.getSession().invalidate();
// Neue Session erstellen
HttpSession newSession = request.getSession(true);
// User authentifizieren
...
}
// Rate Limiting für Login
@RateLimiter(name = "loginRateLimiter", fallbackMethod = "loginFallback")
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// Login-Logik
}
public ResponseEntity<?> loginFallback(LoginRequest request, Exception e) {
return ResponseEntity.status(429)
.body("Too many login attempts. Try again later.");
}
Architektonische Gegenmaßnahmen
- MFA: TOTP, WebAuthn, Push-Notifications
- Passwordless: Magic Links, Passkeys
- Account Lockout: Nach X Fehlversuchen (aber Achtung: DoS-Vektor)
- Credential Stuffing Detection: Device Fingerprinting, Anomaly Detection
- Identity Provider: Keycloak, Auth0, Okta – nicht selbst bauen
A08Software and Data Integrity Failures
Diese Kategorie umfasst Angriffe auf Ihre Build-Pipeline, unsichere Deserialisierung und fehlende Integritätsprüfungen.
CI/CD Pipeline Security
- Kompromittierte Dependencies: Malicious Package in npm/PyPI
- Build-Server-Zugriff: Wer kann Pipelines ändern?
- Artifact-Manipulation: Wird das Artifact signiert?
- Secrets in Logs: CI/CD-Logs mit Credentials
# Supply Chain Security: Package Integrity
# package-lock.json enthält Hashes
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDE..."
}
# npm ci (nicht npm install) prüft Hashes
npm ci --ignore-scripts
Unsichere Deserialisierung
// GEFÄHRLICH: Java Deserialization
ObjectInputStream ois = new ObjectInputStream(userInput);
Object obj = ois.readObject(); // Remote Code Execution möglich!
// SICHERER: JSON mit explizitem Typ
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL
);
// Besser: Gar keine Default Typing, explizite DTOs
ObjectInputStream.readObject() auf User-Input anwenden, haben Sie wahrscheinlich eine Remote Code Execution. Java-Serialisierung sollte für externe Daten nie verwendet werden.
A09Security Logging and Alerting Failures
Wenn Sie nicht wissen, dass Sie angegriffen werden, können Sie nicht reagieren. Fehlende Logs bedeuten: Kein Incident Response möglich.
Was geloggt werden muss
- Authentication Events: Login, Logout, fehlgeschlagene Versuche
- Authorization Failures: Zugriff verweigert
- Input Validation Failures: Potentielle Injection-Versuche
- Application Errors: Unerwartete Exceptions
- Sensitive Operations: Passwort-Änderungen, Admin-Aktionen
// Security Event Logging
@EventListener
public void onAuthenticationFailure(AuthenticationFailureBadCredentialsEvent event) {
String username = event.getAuthentication().getName();
String ip = getClientIp();
log.warn("SECURITY: Failed login attempt | user={} | ip={} | timestamp={}",
username, ip, Instant.now());
// Optional: Alerting bei Anomalien
if (failedLoginTracker.isAnomaly(ip)) {
alertService.sendAlert("Brute-force attempt from " + ip);
}
}
Was NICHT geloggt werden darf
- Passwörter (auch nicht im Fehlerfall)
- Session Tokens
- API Keys
- Kreditkartennummern
- Sensitive PII (ohne Maskierung)
// FALSCH: Passwort im Log
log.info("Login attempt: user={}, password={}", username, password);
// RICHTIG: Nur relevante Info
log.info("Login attempt: user={}, success={}", username, success);
// Sensitive Daten maskieren
log.info("Payment processed: card=****{}", last4Digits);
A10Mishandling of Exceptional Conditions
Neu in 2025. SSRF ist keine eigene Kategorie mehr – es wurde in A01 (Broken Access Control) integriert. A10 adressiert stattdessen Fehlerzustände wie unsicheres Exception-Handling, Race Conditions und Resource Exhaustion.
Was dazugehört
- Unhandled Exceptions: Crashes offenbaren interne Details
- Race Conditions: Time-of-check to time-of-use (TOCTOU)
- Resource Exhaustion: Memory Leaks, File Handle Exhaustion
- Improper Error Handling: Unterschiedliche Fehler für unterschiedliche Zustände (Information Disclosure)
- Deadlocks: System wird unresponsive
// Race Condition: TOCTOU
// FALSCH
if (file.exists()) {
// Zwischen check und use kann sich alles ändern
file.delete(); // Was wenn jemand anderes die Datei ersetzt hat?
}
// RICHTIG: Atomare Operation
try {
Files.delete(path);
} catch (NoSuchFileException e) {
// OK, war schon weg
}
// Information Disclosure durch unterschiedliche Fehlermeldungen
// FALSCH
if (!userExists(username)) {
throw new AuthException("User not found"); // Verrät: User existiert nicht
}
if (!passwordMatches(password)) {
throw new AuthException("Wrong password"); // Verrät: User existiert
}
// RICHTIG: Einheitliche Fehlermeldung
if (!authenticate(username, password)) {
throw new AuthException("Invalid credentials");
}
// Resource Exhaustion verhindern
// Rate Limiting
@RateLimiter(name = "api", fallbackMethod = "rateLimitFallback")
public Response handleRequest() { ... }
// Timeouts setzen (Beispiel mit RestTemplate, WebClient analog)
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory() {{
setConnectTimeout(5000);
setReadTimeout(10000);
}});
// Memory Limits für Uploads
@PostMapping("/upload")
public void upload(@RequestParam MultipartFile file) {
if (file.getSize() > MAX_FILE_SIZE) {
throw new FileTooLargeException();
}
}
Architektonische Gegenmaßnahmen
- Global Exception Handler: Einheitliche, sichere Fehlerseiten
- Circuit Breaker: Resilience4j für kaskadierende Fehler
- Resource Limits: Timeouts, Memory Limits, Connection Pools
- Atomare Operationen: TOCTOU vermeiden
- Graceful Degradation: System bleibt verfügbar, auch wenn Teile ausfallen
Security-Architektur-Patterns
Die OWASP Top 10 einzeln zu adressieren reicht nicht. Diese übergreifenden Patterns helfen:
Defense in Depth
Mehrere Sicherheitsschichten. Wenn eine versagt, fängt die nächste.
Request Flow mit Defense in Depth:
[Internet]
↓
[WAF] → Filtert bekannte Angriffe
↓
[Load Balancer] → Rate Limiting, DDoS Protection
↓
[API Gateway] → Authentication, API Key Validation
↓
[Application] → Authorization, Input Validation
↓
[Database] → Prepared Statements, Least Privilege
Zero Trust
Vertraue niemandem – auch nicht internen Systemen.
- Jeder Request wird authentifiziert und autorisiert
- Auch interne Kommunikation ist verschlüsselt (mTLS)
- Least Privilege überall
- Continuous Verification
Secure Defaults
- Alles ist verboten, bis es erlaubt wird
- Keine Features standardmäßig aktiviert
- Strikte Content Security Policy
- Minimale Berechtigungen für Service Accounts
Fazit
Die OWASP Top 10 sind keine Checkliste zum Abhaken. Sie sind ein Startpunkt für Security-Bewusstsein in Ihrer Architektur.
Die wichtigsten Erkenntnisse:
- Broken Access Control (A01) – Authorization in jedem Layer, nicht nur im Frontend
- Security Misconfiguration (A02) – Infrastructure as Code, automatisierte Checks
- Supply Chain (A03) – SBOM, Artifact Signing, Dependency Scanning
- Injection (A05) – Parameterized Queries, immer und überall
- Insecure Design (A06) – Security beginnt in der Architektur, nicht im Code
Security ist keine Abteilung und kein Tool. Security ist eine Eigenschaft Ihrer Architektur – oder eben nicht.
Security-Review Ihrer Architektur?
Ich analysiere Ihre Anwendung auf OWASP-Schwachstellen – mit Fokus auf architektonische Verbesserungen, nicht nur Code-Fixes.
Review anfragen