Akzeptanz von zusätzlichen nicht vertrauenswürdigen Daten mit vertrauenswürdigen Daten

Beschreibung

Akzeptanz von zusätzlichen nicht vertrauenswürdigen Daten mit vertrauenswürdigen Daten ist eine Schwachstelle, die auftritt, wenn ein Produkt bei der Verarbeitung vertrauenswürdiger Daten auch alle nicht vertrauenswürdigen Daten akzeptiert, die mit den vertrauenswürdigen Daten enthalten sind, und die nicht vertrauenswürdigen Daten behandelt, als wären sie vertrauenswürdig. Diese Schwäche stellt ein fundamentales Versagen dar, zwischen authentifizierten und nicht authentifizierten Informationen innerhalb eines Datenpakets zu unterscheiden. Häufige Manifestationen umfassen das Akzeptieren zusätzlicher Felder in signierten Nachrichten, die nicht Teil des ursprünglich signierten Inhalts waren, das Vertrauen auf alle Einträge in einer DNS-Antwort, wenn nur bestimmte Einträge authentifiziert wurden, das Akzeptieren zusätzlicher Parameter in authentifizierten API-Antworten und das Einschließen nicht vertrauenswürdiger Metadaten neben vertrauenswürdigen Inhalten. Die Schwachstelle nutzt die Annahme aus, dass wenn einige Daten vertrauenswürdig sind, alle begleitenden Daten ebenfalls vertrauenswürdig sein müssen.

Risiko

Das Akzeptieren nicht vertrauenswürdiger Daten, die mit vertrauenswürdigen Daten gebündelt sind, ermöglicht Angreifern, bösartige Inhalte zu injizieren, die das Vertrauen legitimer Daten erben. Bei der Zertifikatsvalidierung können Angreifer Zertifikate fälschen, indem sie zusätzliche Daten in Signaturen einschließen, die Zertifikatsketten-Manipulation ermöglichen. DNS-Antworten können zusätzliche Einträge über das Abgefragte hinaus enthalten, was Cache-Poisoning-Angriffe ermöglicht, bei denen Angreifer Einträge für Domains injizieren, die sie nicht kontrollieren. API-Antworten, die teilweise signiert sind, ermöglichen Angreifern, unsignierte Felder hinzuzufügen, die die Anwendung als vertrauenswürdig verarbeitet. Firmware-Updates mit partieller Signaturabdeckung ermöglichen bösartige Code-Injektion in unsignierten Abschnitten. Das Risiko wird verstärkt, da Anwendungen oft Vertrauensentscheidungen auf Container-Ebene treffen (z.B. "diese Nachricht ist signiert") anstatt auf individueller Datenelementebene, was eine Vertrauenseskalations-Schwachstelle erzeugt, bei der alle Daten im Container vertraut werden, unabhängig davon, was tatsächlich authentifiziert wurde.

Lösung

Implementieren Sie strikte Datenvalidierung, die zwischen authentifizierten und nicht authentifizierten Teilen von Daten unterscheidet. Für signierte Daten verarbeiten Sie nur Felder, die explizit von der Signatur abgedeckt werden, und lehnen Sie alle zusätzlichen Felder ab oder ignorieren Sie sie. Bei DNS-Antworten cachen Sie nur Einträge, die direkt die Abfrage beantworten, und validieren Sie, dass Authority- und Additional-Abschnitte für die abgefragte Domain relevant sind. Für API-Antworten definieren Sie explizite Schemas und lehnen Sie Antworten mit unerwarteten Feldern ab. Implementieren Sie "Sign-then-Encrypt" anstelle von "Encrypt-then-Sign", um Manipulation der Grenzen signierter Inhalte zu verhindern. Verwenden Sie kryptografische Techniken wie kanonische Serialisierung, die sicherstellen, dass Signaturen genau die beabsichtigten Daten abdecken. Wenden Sie das Prinzip der minimalen Autorität an - vertrauen Sie nur den minimal notwendigen Daten und behandeln Sie alle zusätzlichen Daten als nicht vertrauenswürdig.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleUmfang: Zugriffskontrolle

Angreifer können Zugriffskontrollen umgehen, indem sie unbefugte Daten mit autorisierten Daten bündeln und Zugang zu Ressourcen oder Funktionen erhalten, auf die sie keinen Zugriff haben sollten.
IntegritätUmfang: Integrität

Das Akzeptieren zusätzlicher nicht vertrauenswürdiger Daten ermöglicht Angreifern, Anwendungsverhalten zu modifizieren, bösartige Inhalte zu injizieren oder Datenspeicher mit nicht authentifizierten Informationen zu korrumpieren.
AuthentifizierungUmfang: Authentifizierung

Vertrauensgrenzen-Verletzungen ermöglichen Angreifern, Zertifikate oder Anmeldedaten zu fälschen, indem sie Signaturabdeckung manipulieren, um bösartige Daten einzuschließen.

Beispielcode

Anfälliger Code (Python/Java)

Die folgenden Beispiele demonstrieren das Akzeptieren nicht vertrauenswürdiger Daten mit vertrauenswürdigen Daten:

# Anfällig: Akzeptieren von zusätzlichen nicht vertrauenswürdigen Daten
import json
import hmac
import hashlib
from typing import Dict, Any

# Anfällig: Alle Felder aus signierter Nachricht verarbeiten
def vulnerable_process_signed_message(message_json: str, signature: str,
                                       secret_key: bytes) -> Dict[str, Any]:
    message = json.loads(message_json)

    # Anfällig: Nur Signatur deckt "signed_data" Feld ab
    signed_portion = message.get('signed_data', {})
    expected_sig = hmac.new(
        secret_key,
        json.dumps(signed_portion, sort_keys=True).encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected_sig):
        raise ValueError("Ungültige Signatur")

    # Anfällig: Verarbeitet ALLE Felder, nicht nur signierte
    # Angreifer kann "admin": true zur Nachricht hinzufügen
    return message  # Enthält unsignierte Felder!

# Anfällig: DNS-Antwort mit zusätzlichen Einträgen
class VulnerableDNSCache:
    def __init__(self):
        self.cache = {}

    def process_response(self, query_domain: str, response: Dict):
        # Anfällig: Alle Einträge aus Antwort cachen
        # auch wenn sie nicht Teil der Abfrage waren

        # Answer-Sektion - was wir abgefragt haben
        for record in response.get('answers', []):
            self.cache[record['name']] = record['address']

        # Anfällig: Auch Authority-Sektion cachen
        for record in response.get('authority', []):
            self.cache[record['name']] = record['ns']

        # Anfällig: Additional-Sektion cachen (kann injiziert werden)
        for record in response.get('additional', []):
            # Angreifer kann hier Einträge für beliebige Domains injizieren
            self.cache[record['name']] = record['address']

# Anfällig: API-Antwort mit zusätzlichen Feldern
def vulnerable_process_api_response(response: Dict, signature: str,
                                     public_key) -> Dict:
    # Antwortstruktur:
    # {
    #   "user_id": "123",
    #   "timestamp": "2024-01-01T00:00:00Z",
    #   "signature_covers": ["user_id", "timestamp"]
    # }

    # Anfällig: Nur bestimmte Felder verifizieren
    signed_data = {k: response[k] for k in response.get('signature_covers', [])}

    if not verify_signature(signed_data, signature, public_key):
        raise ValueError("Ungültige Signatur")

    # Anfällig: Gesamte Antwort einschließlich unsignierter Felder zurückgeben
    # Angreifer fügt hinzu: "is_admin": true, "permissions": ["all"]
    return response

# Anfällig: Zertifikat mit zusätzlichen Extensions
def vulnerable_validate_certificate(cert):
    # Anfällig: Nur Kernfelder prüfen, nicht alle Extensions
    if not verify_ca_signature(cert):
        raise ValueError("Ungültige CA-Signatur")

    if cert.not_before > now() or cert.not_after < now():
        raise ValueError("Zertifikat abgelaufen")

    # Anfällig: Alle Extensions verarbeiten, einschließlich nicht authentifizierter
    # X.509 Extensions könnten angreifer-injizierte Daten enthalten
    for extension in cert.extensions:
        apply_extension(extension)  # Enthält nicht vertrauenswürdige Extensions

    return True

# Anfällig: Firmware-Update mit partieller Abdeckung
def vulnerable_apply_firmware(firmware_package: bytes, signature: bytes,
                               public_key):
    # Paketformat: [header: 256 bytes][signed_code][extra_data]
    header = firmware_package[:256]
    code_length = int.from_bytes(header[0:4], 'big')
    signed_code = firmware_package[256:256+code_length]
    extra_data = firmware_package[256+code_length:]  # Unsigniert!

    # Anfällig: Nur signed_code wird verifiziert
    if not verify_signature(signed_code, signature, public_key):
        raise ValueError("Ungültige Signatur")

    # Anfällig: Sowohl signierte als auch unsignierte Daten anwenden
    apply_code(signed_code)
    apply_config(extra_data)  # Anfällig: Diese Daten sind NICHT signiert!
// Anfällig: Akzeptieren von zusätzlichen nicht vertrauenswürdigen Daten in Java
import java.util.*;
import javax.crypto.*;

public class VulnerableExtraneousData {

    // Anfällig: Alle JSON-Felder aus signierter Nachricht verarbeiten
    public Map<String, Object> vulnerableProcessMessage(
            String messageJson, String signature, SecretKey key)
            throws Exception {

        Map<String, Object> message = parseJson(messageJson);
        Map<String, Object> signedData =
            (Map<String, Object>) message.get("signed_data");

        // Anfällig: Nur signed_data ist authentifiziert
        String expectedSig = computeHmac(signedData, key);

        if (!MessageDigest.isEqual(
                signature.getBytes(), expectedSig.getBytes())) {
            throw new SecurityException("Ungültige Signatur");
        }

        // Anfällig: Gesamte Nachricht einschließlich unsignierter Felder zurückgeben
        return message;  // Enthält angreifer-injizierte Felder
    }

    // Anfällig: XML-Signatur mit zusätzlichen Elementen
    public Document vulnerableProcessSignedXml(Document doc) throws Exception {
        // XML-Struktur:
        // <message>
        //   <SignedInfo>...</SignedInfo>  <!-- Signiert -->
        //   <data>...</data>              <!-- Signiert (referenziert) -->
        //   <extra>...</extra>            <!-- NICHT signiert! -->
        // </message>

        XMLSignature signature = new XMLSignature(doc);

        // Anfällig: Validiert nur SignedInfo-Referenzen
        if (!signature.validate()) {
            throw new SecurityException("Ungültige XML-Signatur");
        }

        // Anfällig: Gesamtes Dokument einschließlich unsignierter Elemente verarbeiten
        return doc;  // Enthält unsigniertes <extra> Element
    }

    // Anfällig: Token mit zusätzlichen Claims
    public Map<String, Object> vulnerableProcessToken(String token, Key key) {
        // Token: header.payload.signature
        String[] parts = token.split("\\.");

        Map<String, Object> header = decodeBase64Json(parts[0]);
        Map<String, Object> payload = decodeBase64Json(parts[1]);

        // Anfällig: Signatur deckt nur ursprüngliche Payload ab
        // Aber Angreifer kann Base64 modifizieren um zusätzliche Claims einzuschließen
        // durch JSON-Parsing-Eigenheiten oder Whitespace-Injektion

        String signedPortion = parts[0] + "." + parts[1];
        if (!verifySignature(signedPortion, parts[2], key)) {
            throw new SecurityException("Ungültige Signatur");
        }

        // Anfällig: Parser könnte zusätzliche Daten einschließen
        return payload;
    }

    // Anfällig: Konfigurationsdatei mit signierten und unsignierten Abschnitten
    public void vulnerableLoadConfig(byte[] configData, byte[] signature,
                                      PublicKey publicKey) throws Exception {

        // Konfigurationsformat: [signed_section_length:4][signed][unsigned]
        int signedLength = ByteBuffer.wrap(configData, 0, 4).getInt();
        byte[] signedSection = Arrays.copyOfRange(configData, 4, 4 + signedLength);
        byte[] unsignedSection = Arrays.copyOfRange(
            configData, 4 + signedLength, configData.length);

        // Anfällig: Nur signierten Abschnitt verifizieren
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        sig.update(signedSection);

        if (!sig.verify(signature)) {
            throw new SecurityException("Ungültige Konfigurationssignatur");
        }

        // Anfällig: Beide Abschnitte anwenden
        applySignedConfig(signedSection);
        applyUnsignedConfig(unsignedSection);  // Angreifer-kontrolliert!
    }

    // Anfällig: HTTP-Antwort mit zusätzlichen Headern
    public void vulnerableProcessResponse(HttpResponse response,
                                           String expectedBodyHash) {
        // Anfällig: Nur Body wird auf Integrität geprüft
        String actualHash = sha256(response.getBody());

        if (!actualHash.equals(expectedBodyHash)) {
            throw new SecurityException("Body-Integritätsprüfung fehlgeschlagen");
        }

        // Anfällig: Header verarbeiten die nicht auf Integrität geprüft wurden
        String redirectUrl = response.getHeader("X-Redirect-To");
        if (redirectUrl != null) {
            redirect(redirectUrl);  // Angreifer-injizierter Header
        }
    }
}

Korrigierter Code (Python/Java)

# Korrigiert: Nur authentifizierte Daten verarbeiten
import json
import hmac
import hashlib
from typing import Dict, Any, Set

# Korrigiert: Nur signierte Felder zurückgeben
def secure_process_signed_message(message_json: str, signature: str,
                                   secret_key: bytes) -> Dict[str, Any]:
    message = json.loads(message_json)

    # Korrigiert: Nur den signierten Teil extrahieren
    signed_portion = message.get('signed_data', {})
    expected_sig = hmac.new(
        secret_key,
        json.dumps(signed_portion, sort_keys=True).encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected_sig):
        raise ValueError("Ungültige Signatur")

    # Korrigiert: Nur die signierten Daten zurückgeben
    return signed_portion  # Nur authentifizierte Daten

# Korrigiert: DNS-Cache mit strikter Antwortvalidierung
class SecureDNSCache:
    def __init__(self):
        self.cache = {}

    def process_response(self, query_domain: str, response: Dict):
        # Korrigiert: Nur Einträge cachen, die die Abfrage beantworten

        for record in response.get('answers', []):
            # Korrigiert: Verifizieren dass Eintrag für abgefragte Domain ist
            if self._is_valid_answer(query_domain, record):
                self.cache[record['name']] = record['address']

        # Korrigiert: Authority-Sektion nur für Zone der abgefragten Domain
        for record in response.get('authority', []):
            if self._is_in_bailiwick(query_domain, record):
                self.cache[record['name']] = record['ns']

        # Korrigiert: Additional-Sektion nur für bereits vertrauenswürdige Namen
        allowed_names = {query_domain} | self._get_ns_names(response)
        for record in response.get('additional', []):
            # Korrigiert: Nur cachen wenn von vertrauenswürdigen Einträgen referenziert
            if record['name'] in allowed_names:
                self.cache[record['name']] = record['address']

    def _is_valid_answer(self, query_domain: str, record: Dict) -> bool:
        # Eintrag muss für abgefragte Domain oder gültige CNAME-Kette sein
        return record['name'] == query_domain or \
               record['name'].endswith('.' + query_domain)

    def _is_in_bailiwick(self, query_domain: str, record: Dict) -> bool:
        # Authority muss für übergeordnete Zone der abgefragten Domain sein
        parts = query_domain.split('.')
        for i in range(len(parts)):
            zone = '.'.join(parts[i:])
            if record['name'] == zone:
                return True
        return False

    def _get_ns_names(self, response: Dict) -> Set[str]:
        return {r['ns'] for r in response.get('authority', [])}

# Korrigiert: API-Antwort mit striktem Schema
def secure_process_api_response(response: Dict, signature: str,
                                 public_key, expected_schema: Set[str]) -> Dict:
    # Korrigiert: Exakt erwartete Felder definieren
    SIGNED_FIELDS = {'user_id', 'timestamp', 'action'}

    # Korrigiert: Nur erwartete signierte Felder extrahieren
    signed_data = {}
    for field in SIGNED_FIELDS:
        if field not in response:
            raise ValueError(f"Erforderliches Feld fehlt: {field}")
        signed_data[field] = response[field]

    if not verify_signature(signed_data, signature, public_key):
        raise ValueError("Ungültige Signatur")

    # Korrigiert: Nur signierte, validierte Felder zurückgeben
    return signed_data

# Korrigiert: Zertifikat mit validierten Extensions
def secure_validate_certificate(cert):
    if not verify_ca_signature(cert):
        raise ValueError("Ungültige CA-Signatur")

    if cert.not_before > now() or cert.not_after < now():
        raise ValueError("Zertifikat abgelaufen")

    # Korrigiert: Nur bekannte, kritische Extensions verarbeiten
    KNOWN_EXTENSIONS = {
        'basic_constraints',
        'key_usage',
        'subject_alt_name'
    }

    for extension in cert.extensions:
        if extension.oid in KNOWN_EXTENSIONS:
            # Korrigiert: Nur bekannte Extensions verarbeitet
            apply_extension(extension)
        elif extension.critical:
            # Korrigiert: Unbekannte kritische Extensions ablehnen
            raise ValueError(f"Unbekannte kritische Extension: {extension.oid}")
        # Korrigiert: Unbekannte nicht-kritische Extensions werden ignoriert

    return True

# Korrigiert: Firmware-Update mit vollständiger Abdeckung
def secure_apply_firmware(firmware_package: bytes, signature: bytes,
                           public_key):
    # Korrigiert: Signatur deckt GESAMTES Paket ab
    if not verify_signature(firmware_package, signature, public_key):
        raise ValueError("Ungültige Signatur")

    # Korrigiert: Nach Verifizierung parsen
    header = firmware_package[:256]
    code_length = int.from_bytes(header[0:4], 'big')
    code = firmware_package[256:256+code_length]
    config = firmware_package[256+code_length:]

    # Korrigiert: Alle Daten wurden verifiziert
    apply_code(code)
    apply_config(config)  # Sicher: war Teil des signierten Pakets
// Korrigiert: Nur authentifizierte Daten in Java verarbeiten
import java.util.*;
import javax.crypto.*;

public class SecureExtraneousData {

    // Korrigiert: Nur signierte Felder zurückgeben
    public Map<String, Object> secureProcessMessage(
            String messageJson, String signature, SecretKey key)
            throws Exception {

        Map<String, Object> message = parseJson(messageJson);
        Map<String, Object> signedData =
            (Map<String, Object>) message.get("signed_data");

        if (signedData == null) {
            throw new SecurityException("signed_data fehlt");
        }

        String expectedSig = computeHmac(signedData, key);

        if (!MessageDigest.isEqual(
                signature.getBytes(), expectedSig.getBytes())) {
            throw new SecurityException("Ungültige Signatur");
        }

        // Korrigiert: Nur den signierten Teil zurückgeben
        return new HashMap<>(signedData);  // Nur authentifizierte Daten
    }

    // Korrigiert: XML-Signatur mit strikter Element-Validierung
    public Map<String, Object> secureProcessSignedXml(Document doc)
            throws Exception {

        XMLSignature signature = new XMLSignature(doc);

        if (!signature.validate()) {
            throw new SecurityException("Ungültige XML-Signatur");
        }

        // Korrigiert: Nur signierte Referenzen extrahieren
        Set<String> signedElements = signature.getSignedElementIds();
        Map<String, Object> result = new HashMap<>();

        for (String elementId : signedElements) {
            Element element = doc.getElementById(elementId);
            if (element != null) {
                result.put(element.getTagName(), extractContent(element));
            }
        }

        // Korrigiert: Nur von Signatur abgedeckte Elemente zurückgeben
        return result;
    }

    // Korrigiert: Token mit exakter Payload-Validierung
    public Map<String, Object> secureProcessToken(String token, Key key) {
        String[] parts = token.split("\\.");
        if (parts.length != 3) {
            throw new SecurityException("Ungültiges Token-Format");
        }

        // Korrigiert: Signatur über exakte Eingabe verifizieren
        String signedPortion = parts[0] + "." + parts[1];
        if (!verifySignature(signedPortion, parts[2], key)) {
            throw new SecurityException("Ungültige Signatur");
        }

        // Korrigiert: Mit striktem JSON-Parser parsen
        Map<String, Object> payload = strictJsonParse(
            new String(Base64.getUrlDecoder().decode(parts[1]))
        );

        // Korrigiert: Nur erwartete Claims erlauben
        Set<String> allowedClaims = Set.of(
            "sub", "iat", "exp", "iss", "aud"
        );

        Map<String, Object> filtered = new HashMap<>();
        for (String claim : allowedClaims) {
            if (payload.containsKey(claim)) {
                filtered.put(claim, payload.get(claim));
            }
        }

        // Korrigiert: Nur erlaubte, verifizierte Claims zurückgeben
        return filtered;
    }

    // Korrigiert: Konfigurationsdatei mit Signatur über alles
    public void secureLoadConfig(byte[] configData, byte[] signature,
                                  PublicKey publicKey) throws Exception {

        // Korrigiert: Signatur über GESAMTE Konfiguration verifizieren
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        sig.update(configData);

        if (!sig.verify(signature)) {
            throw new SecurityException("Ungültige Konfigurationssignatur");
        }

        // Korrigiert: Nur nach vollständiger Verifizierung parsen
        Config config = parseConfig(configData);
        applyConfig(config);  // Sicher: alle Daten waren signiert
    }

    // Korrigiert: HTTP-Antwort mit authentifizierten Headern
    public void secureProcessResponse(HttpResponse response,
                                       String expectedHash,
                                       Set<String> signedHeaders) {
        // Korrigiert: Hash deckt Body UND kritische Header ab
        StringBuilder toHash = new StringBuilder();

        for (String header : signedHeaders) {
            toHash.append(header).append(":")
                  .append(response.getHeader(header)).append("\n");
        }
        toHash.append(response.getBody());

        String actualHash = sha256(toHash.toString());

        if (!actualHash.equals(expectedHash)) {
            throw new SecurityException("Antwort-Integritätsprüfung fehlgeschlagen");
        }

        // Korrigiert: Nur Header verarbeiten die auf Integrität geprüft wurden
        for (String header : signedHeaders) {
            processHeader(header, response.getHeader(header));
        }
    }

    private Map<String, Object> strictJsonParse(String json) {
        // Strikten JSON-Parser verwenden der keine Duplikate
        // oder nachfolgende Daten erlaubt
        return new StrictJsonParser().parse(json);
    }
}

Die Korrektur stellt sicher, dass nur explizit authentifizierte Daten verarbeitet werden und alle zusätzlichen Daten abgelehnt oder ignoriert werden.


Ausgenutzt in der Praxis

DNS-Cache-Poisoning (CVE-2002-0018)

DNS-Resolver akzeptierten zusätzliche Einträge in Antworten über das Abgefragte hinaus, was Angreifern ermöglichte, Einträge für beliebige Domains zu injizieren.

Zertifikats-Signatur-Fälschung (CVE-2006-5462)

Zusätzliche Daten in Zertifikatssignaturen ermöglichten das Fälschen von Zertifikatsketten durch Manipulation von Daten außerhalb des signierten Teils.


Tools zum Testen/Ausnutzen

  • Burp Suite — Zusätzliche Felder in API-Antworten und signierte Nachrichten injizieren.

  • DNS-Testwerkzeuge — DNS-Resolver-Verhalten mit zusätzlichen Einträgen testen.

  • jwt_tool — JWT-Handling mit zusätzlichen Claims testen.


CVE-Beispiele


Referenzen

  1. MITRE Corporation. "CWE-349: Acceptance of Extraneous Untrusted Data With Trusted Data." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/349.html

  2. OWASP Foundation. "Injection Flaws." https://owasp.org/www-community/Injection_Flaws

  3. RFC 5155. "DNS Security (DNSSEC) Hashed Authenticated Denial of Existence." https://tools.ietf.org/html/rfc5155