Unsachgemäße Verwendung eines Validierungs-Frameworks
Beschreibung
Unsachgemäße Verwendung eines Validierungs-Frameworks tritt auf, wenn ein Produkt ein von der Quellsprache oder einer unabhängigen Bibliothek bereitgestelltes Eingabevalidierungs-Framework nicht verwendet oder falsch verwendet. Moderne Programmiersprachen und Frameworks bieten typischerweise integrierte Eingabevalidierungsfähigkeiten, die darauf ausgelegt sind, Validierungsprozesse zu rationalisieren und Fehler zu reduzieren. Diese Tools überprüfen automatisch Eingaben gegen festgelegte Anforderungen und leiten die Ausführung bei ungültigen Daten an Fehlerbehandler weiter. Das Nichtnutzen oder Fehlverwenden dieser Frameworks führt zu inkonsistenter Validierung, erhöhter Wahrscheinlichkeit von Schwachstellen und reduzierter Wartbarkeit.
Risiko
Die unsachgemäße Verwendung von Validierungs-Frameworks hat erhebliche Sicherheitsauswirkungen. Manuelle Validierung ist fehleranfälliger als Framework-Validierung. Inkonsistente Validierung in der gesamten Codebasis schafft Sicherheitslücken. Möglichkeiten zur Umgehung der Eingabevalidierung nehmen zu. Häufige Schwachstellen wie XSS, SQL-Injection und Command-Injection werden wahrscheinlicher. Validierungslogik ist verstreut und schwer zu prüfen. Änderungen an Validierungsregeln werden möglicherweise nicht konsistent angewendet. Framework-Sicherheitsupdates werden möglicherweise nicht genutzt. Benutzerdefinierter Validierungscode kann unentdeckte Fehler haben.
Lösung
Verwenden Sie etablierte Validierungs-Frameworks, die von der Sprache oder renommierten Bibliotheken bereitgestellt werden. Konfigurieren Sie Validierungsregeln wo möglich deklarativ. Wenden Sie Validierung konsistent an Systemgrenzen an. Verwenden Sie die integrierten Validatoren des Frameworks, bevor Sie benutzerdefinierte erstellen. Stellen Sie sicher, dass Validierungsfehler ordnungsgemäß behandelt werden. Halten Sie Validierungs-Frameworks aktuell. Dokumentieren Sie Validierungsanforderungen in Schema-Definitionen. Testen Sie Validierungsregeln gründlich. Verwenden Sie serverseitige Validierung auch bei clientseitiger Validierung. Zentralisieren Sie Validierungslogik wo möglich.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Unerwarteter Zustand - Ungeprüfte Eingaben führen zu Cross-Site-Scripting, Prozesssteuerung, SQL-Injection-Schwachstellen und anderen Injection-Angriffen. |
| Verfügbarkeit | Bereich: Verfügbarkeit Denial of Service - Unsachgemäße Validierung kann fehlerhaft formatierte Eingaben zulassen, die die Anwendung zum Absturz bringen. |
Beispielcode
Verwundbarer Code
// Verwundbar: Java Bean Validation Framework nicht verwendet
public class UserController {
// Manuelle Validierung - fehleranfällig und inkonsistent
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody Map<String, Object> userData) {
// Verstreute manuelle Validierung - leicht Fälle zu übersehen
String username = (String) userData.get("username");
String email = (String) userData.get("email");
String password = (String) userData.get("password");
Integer age = (Integer) userData.get("age");
// Inkonsistente Validierung - verschiedene Muster an jeder Stelle
if (username == null || username.isEmpty()) {
return ResponseEntity.badRequest().body("Benutzername erforderlich");
}
if (username.length() < 3 || username.length() > 50) {
return ResponseEntity.badRequest().body("Benutzername muss 3-50 Zeichen haben");
}
// Fehlende Validierung für Sonderzeichen im Benutzernamen!
if (email == null || !email.contains("@")) { // Unzureichende E-Mail-Validierung
return ResponseEntity.badRequest().body("Ungültige E-Mail");
}
// Passwort-Validierung verstreut und unvollständig
if (password == null) {
return ResponseEntity.badRequest().body("Passwort erforderlich");
}
// Fehlt: Längenprüfung, Komplexitätsanforderungen!
if (age != null && age < 0) { // Was ist mit Alter > 150?
return ResponseEntity.badRequest().body("Ungültiges Alter");
}
// Benutzer verarbeiten - Validierung kann Lücken haben
return createUserInternal(username, email, password, age);
}
// Anderer Endpoint mit unterschiedlicher (inkonsistenter) Validierung
@PutMapping("/users/{id}")
public ResponseEntity<?> updateUser(@PathVariable Long id,
@RequestBody Map<String, Object> userData) {
String email = (String) userData.get("email");
// Andere E-Mail-Validierung hier!
if (email != null && email.indexOf("@") < 0) { // Andere Prüfung
return ResponseEntity.badRequest().body("Ungültige E-Mail");
}
// Passwort-Validierung fehlt hier komplett!
return updateUserInternal(id, userData);
}
}
# Verwundbar: Python-Validierungsbibliotheken wie Pydantic oder Marshmallow nicht verwendet
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/orders', methods=['POST'])
def create_order():
# Manuelle Validierung - verstreut und unvollständig
data = request.json
# Keine Schema-Validierung - akzeptiert jede Struktur
if not data:
return jsonify({'error': 'Keine Daten'}), 400
# Manuelle Feldvalidierung - leicht Felder zu übersehen
customer_id = data.get('customer_id')
if not customer_id:
return jsonify({'error': 'customer_id erforderlich'}), 400
# Typprüfung fehlt - was wenn customer_id keine Ganzzahl ist?
items = data.get('items')
if not items:
return jsonify({'error': 'items erforderlich'}), 400
# Keine Validierung der Artikelstruktur!
# Welche Felder sollte jeder Artikel haben?
# Was sind gültige Werte?
total = data.get('total')
if total is not None:
# Was wenn total negativ ist?
# Was wenn total nicht zu items passt?
pass
# Bestellung verarbeiten - möglicherweise mit ungültigen Daten
return process_order(data)
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.json
# Anderer Validierungsansatz als beim Bestellungs-Endpoint
email = data.get('email', '')
# Regex-Validierung fehleranfällig
import re
if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+
```javascript
// Verwundbar: Validierungsbibliotheken wie Joi oder Yup nicht verwendet
const express = require('express');
const app = express();
app.post('/api/products', (req, res) => {
const { name, price, category, description } = req.body;
// Manuelle Validierung - unvollständig und inkonsistent
if (!name) {
return res.status(400).json({ error: 'Name erforderlich' });
}
// Keine Längenvalidierung für name
if (!price) {
return res.status(400).json({ error: 'Preis erforderlich' });
}
// Typkonvertierungsprobleme - price könnte ein String sein
if (price < 0) { // Was wenn price "abc" ist?
return res.status(400).json({ error: 'Ungültiger Preis' });
}
// category-Validierung fehlt
// description - keine Längenbegrenzung = potenzielle DoS
// XSS-Risiko - keine Bereinigung der Eingaben
createProduct({ name, price, category, description });
res.json({ success: true });
});
// Anderer Endpoint, anderer Validierungsstil
app.put('/api/products/:id', (req, res) => {
const { name, price } = req.body;
// Inkonsistente Validierung - erlaubt hier leeren Namen!
if (price !== undefined && typeof price !== 'number') {
return res.status(400).json({ error: 'Preis muss Zahl sein' });
}
// Keine Validierung des id-Parameters!
updateProduct(req.params.id, { name, price });
res.json({ success: true });
});
Lösung
// Behoben: Java Bean Validation (JSR-380) Framework verwendet
import javax.validation.Valid;
import javax.validation.constraints.*;
// Validierungsregeln deklarativ definieren
public class UserRequest {
@NotBlank(message = "Benutzername ist erforderlich")
@Size(min = 3, max = 50, message = "Benutzername muss 3-50 Zeichen haben")
@Pattern(regexp = "^[a-zA-Z0-9_]+$",
message = "Benutzername kann nur Buchstaben, Zahlen und Unterstriche enthalten")
private String username;
@NotBlank(message = "E-Mail ist erforderlich")
@Email(message = "Ungültiges E-Mail-Format")
private String email;
@NotBlank(message = "Passwort ist erforderlich")
@Size(min = 8, max = 128, message = "Passwort muss 8-128 Zeichen haben")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).*$",
message = "Passwort muss Groß-, Kleinbuchstaben und Zahl enthalten")
private String password;
@Min(value = 0, message = "Alter kann nicht negativ sein")
@Max(value = 150, message = "Alter muss realistisch sein")
private Integer age;
// Getter und Setter...
}
@RestController
@Validated
public class UserController {
// Framework behandelt Validierung automatisch
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody UserRequest request,
BindingResult bindingResult) {
// Framework hat bereits validiert - auf Fehler prüfen
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}
// Alle Validierungen bestanden - sicher zu verarbeiten
return createUserInternal(request);
}
// Konsistente Validierung über gemeinsame UserRequest-Klasse
@PutMapping("/users/{id}")
public ResponseEntity<?> updateUser(@PathVariable @Positive Long id,
@Valid @RequestBody UserRequest request) {
// Gleiche Validierungsregeln werden automatisch angewendet
return updateUserInternal(id, request);
}
// Benutzerdefinierte Validierung mit Framework-Integration
@PostMapping("/users/batch")
public ResponseEntity<?> createUsers(@Valid @RequestBody List<@Valid UserRequest> requests) {
// Framework validiert jeden Eintrag in der Liste
return createUsersInternal(requests);
}
}
// Globaler Exception-Handler für Validierungsfehler
@ControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, List<String>>> handleValidationErrors(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(Map.of("errors", errors));
}
}
# Behoben: Pydantic für Validierung verwenden
from pydantic import BaseModel, Field, EmailStr, validator
from typing import List, Optional
from fastapi import FastAPI, HTTPException
app = FastAPI()
# Validierungsschema mit Pydantic definieren
class OrderItem(BaseModel):
product_id: int = Field(..., gt=0, description="Produkt-ID muss positiv sein")
quantity: int = Field(..., ge=1, le=100, description="Menge 1-100")
unit_price: float = Field(..., gt=0, description="Preis muss positiv sein")
class OrderRequest(BaseModel):
customer_id: int = Field(..., gt=0)
items: List[OrderItem] = Field(..., min_items=1, max_items=50)
total: Optional[float] = Field(None, ge=0)
notes: Optional[str] = Field(None, max_length=500)
@validator('total')
def validate_total(cls, v, values):
"""Validiere dass total der Artikelsumme entspricht falls angegeben."""
if v is not None and 'items' in values:
calculated = sum(item.quantity * item.unit_price
for item in values['items'])
if abs(v - calculated) > 0.01:
raise ValueError('Total entspricht nicht der Artikelsumme')
return v
class Config:
# Zusätzliche Validierungskonfiguration
extra = 'forbid' # Unbekannte Felder ablehnen
class UserRequest(BaseModel):
username: str = Field(..., min_length=3, max_length=50,
regex=r'^[a-zA-Z0-9_]+
```javascript
// Behoben: Joi-Validierungsbibliothek verwenden
const express = require('express');
const Joi = require('joi');
const app = express();
app.use(express.json());
// Validierungsschemata definieren
const productSchema = Joi.object({
name: Joi.string()
.min(1)
.max(100)
.required()
.trim()
.pattern(/^[a-zA-Z0-9\s\-]+$/)
.messages({
'string.empty': 'Name ist erforderlich',
'string.max': 'Name darf 100 Zeichen nicht überschreiten',
'string.pattern.base': 'Name enthält ungültige Zeichen'
}),
price: Joi.number()
.positive()
.precision(2)
.max(1000000)
.required()
.messages({
'number.positive': 'Preis muss positiv sein',
'number.max': 'Preis überschreitet Maximum'
}),
category: Joi.string()
.valid('electronics', 'clothing', 'food', 'other')
.required(),
description: Joi.string()
.max(1000)
.optional()
.trim()
});
// Validierungs-Middleware-Fabrik
const validate = (schema) => {
return (req, res, next) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false, // Alle Fehler zurückgeben
stripUnknown: true // Unbekannte Felder entfernen
});
if (error) {
const errors = error.details.map(d => d.message);
return res.status(400).json({ errors });
}
req.body = value; // Bereinigte Werte verwenden
next();
};
};
// Endpoints mit Schema-Validierung
app.post('/api/products', validate(productSchema), (req, res) => {
// Daten wurden von Joi validiert und bereinigt
const { name, price, category, description } = req.body;
createProduct({ name, price, category, description });
res.json({ success: true });
});
// Gleiches Schema für Updates (mit optionalen Feldern)
const productUpdateSchema = productSchema.fork(
['name', 'price', 'category'],
(schema) => schema.optional()
);
app.put('/api/products/:id',
validate(Joi.object({ id: Joi.number().positive().required() }).unknown(true)),
validate(productUpdateSchema),
(req, res) => {
// Sowohl params als auch body validiert
updateProduct(req.params.id, req.body);
res.json({ success: true });
}
);
CVE-Beispiele
Während diese CWE selbst nicht direkt auf CVEs abgebildet wird, ist unsachgemäße Eingabevalidierung eine Grundursache vieler Schwachstellenklassen:
- SQL-Injection (CWE-89): Verursacht durch unzureichende Eingabevalidierung
- XSS (CWE-79): Verursacht durch unzureichende Ausgabekodierung und Eingabevalidierung
- Command-Injection (CWE-78): Verursacht durch unzureichende Eingabevalidierung
Verwandte CWEs
- CWE-20: Unsachgemäße Eingabevalidierung (übergeordnet)
- CWE-1215: Datenvalidierungsprobleme (Kategorie-Mitglied)
- CWE-102: Struts: Doppelte Validierungsformulare (untergeordnet)
- CWE-105: Struts: Formularfeld ohne Validator (untergeordnet)
- CWE-106: Struts: Plug-in-Framework nicht verwendet (untergeordnet)
Referenzen
- MITRE Corporation. "CWE-1173: Improper Use of Validation Framework." https://cwe.mitre.org/data/definitions/1173.html
- OWASP Input Validation Cheat Sheet
- Java Bean Validation (JSR-380)
- Pydantic-Dokumentation
- Joi-Validierungsbibliothek