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

AuswirkungDetails
IntegritätBereich: Integrität

Unerwarteter Zustand - Ungeprüfte Eingaben führen zu Cross-Site-Scripting, Prozesssteuerung, SQL-Injection-Schwachstellen und anderen Injection-Angriffen.
VerfügbarkeitBereich: 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

  1. MITRE Corporation. "CWE-1173: Improper Use of Validation Framework." https://cwe.mitre.org/data/definitions/1173.html
  2. OWASP Input Validation Cheat Sheet
  3. Java Bean Validation (JSR-380)
  4. Pydantic-Dokumentation
  5. Joi-Validierungsbibliothek