Unzureichende Typunterscheidung

Beschreibung

Unzureichende Typunterscheidung ist eine Schwachstelle, die auftritt, wenn ein Produkt nicht ordnungsgemäß zwischen verschiedenen Arten von Elementen unterscheidet, was zu unsicherem Verhalten führt. Diese Schwäche manifestiert sich, wenn Anwendungen es versäumen, zwischen unterschiedlichen Datentypen, Ereignistypen, Objekttypen oder anderen kategorialen Unterscheidungen zu differenzieren, die sicherheitsrelevante Auswirkungen haben. Häufige Beispiele umfassen das Versäumnis, zwischen benutzer-initiierten Ereignissen und programmatisch generierten (synthetischen) Ereignissen in Benutzeroberflächen zu unterscheiden, verschiedene Dateitypen als äquivalent zu behandeln, wenn sie unterschiedliche Sicherheitseigenschaften haben, nicht zwischen authentifizierten und nicht authentifizierten Anfragen zu differenzieren und Elemente basierend auf partiellen Attributen anstelle vollständiger Typinformationen zu vergleichen. Das Ergebnis ist, dass Sicherheitsmechanismen, die für einen Elementtyp konzipiert sind, umgangen werden können, indem ein anderer Typ substituiert wird, den die Anwendung fälschlicherweise als äquivalent behandelt.

Risiko

Unzureichende Typunterscheidung ermöglicht Angreifern, Sicherheitskontrollen zu umgehen, indem sie das Versagen der Anwendung ausnutzen, zwischen Typen zu differenzieren. In Browser-Kontexten ermöglicht das Versäumnis, zwischen benutzer-initiierten Klicks und synthetischen JavaScript-Ereignissen zu unterscheiden, Clickjacking und automatisierte UI-Interaktionsangriffe. Dateiupload-Systeme, die Dateitypen nicht ordnungsgemäß durch ihren tatsächlichen Inhalt unterscheiden, können erlauben, dass ausführbare Dateien als Bilder hochgeladen werden. Zugriffskontrollsysteme, die nicht zwischen Anforderungstypen unterscheiden, können Berechtigungen falsch anwenden. Wenn Anwendungen Objekte basierend auf unvollständigen Attributen vergleichen, können Angreifer Objekte erstellen, die privilegierten Objekten ähnlich erscheinen, es aber nicht sind, was zu Privilegien-Eskalation oder Datenverlust führt. Das Risiko wird verstärkt, wenn die Typunterscheidung sicherheitskritische Entscheidungen wie Autorisierung, Dateiverwaltung oder Ereignisverarbeitung beeinflusst, wo die Folgen einer Fehlklassifizierung schwerwiegend sein können.

Lösung

Implementieren Sie strikte Typprüfung und -validierung in der gesamten Anwendung. In Benutzeroberflächen verifizieren Sie, dass Ereignisse wirklich benutzer-initiiert sind, bevor sicherheitsrelevante Aktionen verarbeitet werden. Für Dateiverwaltung validieren Sie Dateien nach Inhalt anstatt Erweiterung und pflegen Sie explizite Typinformationen. Beim Vergleichen von Objekten für Sicherheitszwecke vergleichen Sie alle relevanten Attribute einschließlich derer, die Typen unterscheiden. Verwenden Sie starke Typisierung in Programmiersprachen, wo verfügbar, und validieren Sie Typen explizit, wenn starke Typisierung nicht durchgesetzt wird. Implementieren Sie separate Code-Pfade für die Handhabung verschiedener Typen, anstatt zu versuchen, alle Typen generisch zu behandeln. Dokumentieren Sie die Sicherheitsimplikationen von Typunterscheidungen und stellen Sie sicher, dass alle Entwickler verstehen, wann Typprüfung sicherheitskritisch ist. Verwenden Sie explizite Typmarkierungen, die von Angreifern nicht gefälscht werden können.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleUmfang: Zugriffskontrolle

Das Versäumnis, zwischen Benutzertypen, Anforderungstypen oder Berechtigungsstufen zu unterscheiden, kann unbefugten Zugriff oder Privilegien-Eskalation ermöglichen.
IntegritätUmfang: Integrität

Falsche Typbehandlung kann zu Datenkorruption, Verlust von Zugriffskontrolllisten oder unsachgemäßer Änderung sensibler Daten führen.
SonstigeUmfang: Sonstige

Das Versäumnis, Ereignistypen zu unterscheiden, ermöglicht UI-Manipulation, Clickjacking und automatisierte Interaktionsangriffe, die die Benutzereinwilligung umgehen.

Beispielcode

Anfälliger Code (JavaScript/Python)

Die folgenden Beispiele demonstrieren unzureichende Typunterscheidung:

// Anfällig: Keine Unterscheidung zwischen Benutzer- und synthetischen Ereignissen
document.getElementById('deleteButton').addEventListener('click', function(event) {
    // Anfällig: Nicht prüfend ob Ereignis benutzer-initiiert ist
    // Synthetische Ereignisse können dies auslösen
    performDeletion();
});

// Anfällig: Formularübermittlung ohne Ereignistypprüfung
document.querySelector('form').addEventListener('submit', function(event) {
    // Anfällig: Skript-generierte Submit-Ereignisse werden wie Benutzer behandelt
    submitForm();
});

// Anfällig: Alle Dateitypen gleich behandeln
function vulnerableUploadFile(file) {
    // Anfällig: Nur Erweiterung prüfen, nicht tatsächlichen Typ
    if (file.name.endsWith('.jpg') || file.name.endsWith('.png')) {
        uploadImage(file);  // Könnte ausführbar mit falscher Erweiterung sein
    }
}

// Anfällig: Keine Unterscheidung zwischen Anfragequellen
app.post('/api/delete', (req, res) => {
    // Anfällig: Same-Origin und Cross-Origin Anfragen werden identisch behandelt
    // Kein CSRF-Token oder Origin-Prüfung
    deleteResource(req.body.id);
});

// Anfällig: Objektvergleich mit unvollständigen Attributen
function vulnerableCheckPermission(user, requiredRole) {
    // Anfällig: Nur Rollenname prüfen, nicht Rollenquelle/-gültigkeit
    if (user.role === requiredRole) {
        return true;  // Angreifer kann user.role auf beliebigen String setzen
    }
    return false;
}

// Anfällig: Session-Typ nicht unterschieden
function vulnerableGetSession(sessionId) {
    const session = sessions.get(sessionId);
    // Anfällig: Nicht prüfend ob Session Admin vs Benutzer-Typ ist
    // oder ob Session temporär vs persistent ist
    return session;
}

// Anfällig: Nachrichtentyp-Verwirrung
window.addEventListener('message', function(event) {
    const data = event.data;

    // Anfällig: Nachrichtentypen nicht ordnungsgemäß unterscheiden
    if (data.action === 'transfer') {
        // Könnte legitime App-Nachricht oder Frame des Angreifers sein
        performTransfer(data.amount, data.recipient);
    }
});
# Anfällig: Unzureichende Typunterscheidung in Python
from flask import Flask, request
import os

app = Flask(__name__)

# Anfällig: Keine Dateityp-Validierung
@app.route('/upload', methods=['POST'])
def vulnerable_upload():
    file = request.files['file']

    # Anfällig: Dateinamen-Erweiterung vertrauen
    if file.filename.endswith(('.jpg', '.png', '.gif')):
        # Angreifer lädt PHP-Datei als image.jpg hoch
        file.save(os.path.join('/uploads/', file.filename))
        return 'Hochgeladen'

    return 'Ungültiger Dateityp', 400

# Anfällig: Objekttyp-Verwirrung
class VulnerablePermissionCheck:
    def check_access(self, user_obj, resource):
        # Anfällig: Nur Attribut prüfen, nicht Objekttyp
        if hasattr(user_obj, 'is_admin') and user_obj.is_admin:
            return True  # Angreifer erstellt Dict mit is_admin=True

        return False

# Anfällig: Anforderungstyp nicht unterschieden
@app.route('/api/sensitive', methods=['GET', 'POST'])
def vulnerable_api():
    # Anfällig: GET und POST werden gleich behandelt
    # GET-Anfragen könnten CSRF-Schutz umgehen
    return handle_sensitive_action(request.values)

# Anfällig: Unvollständige Attributvergleiche
class User:
    def __init__(self, id, name, role):
        self.id = id
        self.name = name
        self.role = role

def vulnerable_compare_users(user1, user2):
    # Anfällig: Nur Name und Rolle vergleichen, nicht ID
    # Zwei verschiedene Benutzer mit gleichem Namen/Rolle werden als identisch behandelt
    return user1.name == user2.name and user1.role == user2.role

# Anfällig: Token-Typ-Verwirrung
def vulnerable_verify_token(token):
    # Anfällig: Nicht zwischen Access- und Refresh-Tokens unterscheiden
    decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
    # Angreifer verwendet Refresh-Token als Access-Token
    return decoded.get('user_id')

Korrigierter Code (JavaScript/Python)

// Korrigiert: Ordnungsgemäße Typunterscheidung
document.getElementById('deleteButton').addEventListener('click', function(event) {
    // Korrigiert: Prüfen ob Ereignis vertrauenswürdig (benutzer-initiiert) ist
    if (!event.isTrusted) {
        console.warn('Synthetisches Ereignis blockiert');
        return;
    }

    // Korrigiert: Zusätzliche Prüfung auf kürzliche Benutzerinteraktion
    if (!isUserInteractionRecent()) {
        return;
    }

    performDeletion();
});

// Korrigiert: Formularübermittlung mit Ereignisvalidierung
document.querySelector('form').addEventListener('submit', function(event) {
    // Korrigiert: Vertrauenswürdiges Ereignis verifizieren
    if (!event.isTrusted) {
        event.preventDefault();
        return;
    }

    // Korrigiert: Auf versteckte Formularübermittlungen prüfen
    if (!document.hasFocus()) {
        event.preventDefault();
        return;
    }

    submitForm();
});

// Korrigiert: Dateityp-Validierung nach Inhalt
async function secureUploadFile(file) {
    // Korrigiert: Tatsächlichen Dateiinhalt prüfen, nicht nur Erweiterung
    const buffer = await file.arrayBuffer();
    const bytes = new Uint8Array(buffer.slice(0, 4));

    // Magic Bytes prüfen
    const isJpeg = bytes[0] === 0xFF && bytes[1] === 0xD8;
    const isPng = bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E;

    if (!isJpeg && !isPng) {
        throw new Error('Ungültiger Dateityp');
    }

    // Korrigiert: Auch serverseitig validieren
    uploadImage(file);
}

// Korrigiert: Anfrage-Origin-Unterscheidung
app.post('/api/delete', csrfProtection, (req, res) => {
    // Korrigiert: CSRF-Token validiert dass Anfrage von unserem Origin stammt
    // Korrigiert: Origin-Header prüfen
    const origin = req.headers.origin;
    if (!allowedOrigins.includes(origin)) {
        return res.status(403).send('Verboten');
    }

    deleteResource(req.body.id);
});

// Korrigiert: Vollständiger Objektvergleich
function secureCheckPermission(user, requiredRole) {
    // Korrigiert: Benutzerobjekttyp und -quelle verifizieren
    if (!(user instanceof AuthenticatedUser)) {
        return false;  // Muss ordnungsgemäße User-Instanz sein
    }

    // Korrigiert: Rollenobjekt prüfen, nicht nur Namen
    if (!user.roles || !Array.isArray(user.roles)) {
        return false;
    }

    // Korrigiert: Rollenobjekte mit ordnungsgemäßer Validierung vergleichen
    return user.roles.some(role =>
        role instanceof Role &&
        role.name === requiredRole &&
        role.isValid()
    );
}

// Korrigiert: Session-Typ-Unterscheidung
function secureGetSession(sessionId, expectedType) {
    const session = sessions.get(sessionId);

    if (!session) return null;

    // Korrigiert: Session-Typ verifizieren
    if (session.type !== expectedType) {
        return null;  // Falscher Session-Typ
    }

    // Korrigiert: Session-Quelle verifizieren
    if (!session.source || session.source !== 'auth_server') {
        return null;
    }

    return session;
}

// Korrigiert: Nachrichtentyp mit Origin-Prüfung
window.addEventListener('message', function(event) {
    // Korrigiert: Origin verifizieren
    if (!trustedOrigins.includes(event.origin)) {
        return;
    }

    const data = event.data;

    // Korrigiert: Nachrichtenstruktur und -typ verifizieren
    if (typeof data !== 'object' || data === null) {
        return;
    }

    if (!data.messageType || data.messageType !== 'app_internal') {
        return;
    }

    // Korrigiert: Typspezifische Behandlung
    switch(data.action) {
        case 'transfer':
            if (validateTransferMessage(data)) {
                performTransfer(data.amount, data.recipient);
            }
            break;
    }
});
# Korrigiert: Ordnungsgemäße Typunterscheidung in Python
from flask import Flask, request
import magic  # python-magic für Dateityperkennung
from functools import wraps

app = Flask(__name__)

# Korrigiert: Inhaltsbasierte Dateityp-Validierung
@app.route('/upload', methods=['POST'])
def secure_upload():
    file = request.files['file']

    # Korrigiert: Tatsächlichen Dateiinhaltstyp prüfen
    file_content = file.read()
    mime_type = magic.from_buffer(file_content, mime=True)

    allowed_types = {'image/jpeg', 'image/png', 'image/gif'}
    if mime_type not in allowed_types:
        return 'Ungültiger Dateityp', 400

    # Korrigiert: Sicheren Dateinamen generieren
    import uuid
    safe_filename = f"{uuid.uuid4()}.{mime_type.split('/')[-1]}"

    file.seek(0)
    file.save(os.path.join('/uploads/', safe_filename))
    return 'Hochgeladen'

# Korrigiert: Ordnungsgemäße Typprüfung für Berechtigungen
class SecurePermissionCheck:
    def check_access(self, user_obj, resource):
        # Korrigiert: Objekttyp explizit verifizieren
        if not isinstance(user_obj, AuthenticatedUser):
            return False

        # Korrigiert: Prüfen dass Attribut existiert und korrekten Typ hat
        if not hasattr(user_obj, 'is_admin') or \
           not isinstance(user_obj.is_admin, bool):
            return False

        # Korrigiert: Verifizieren dass Benutzer aus gültiger Quelle stammt
        if not user_obj.authenticated_by_server:
            return False

        return user_obj.is_admin

# Korrigiert: HTTP-Methoden unterscheiden
@app.route('/api/sensitive', methods=['POST'])
@csrf_protect
def secure_api():
    # Korrigiert: Nur POST erlaubt, CSRF-Schutz aktiviert
    # Korrigiert: Explizite Methodenprüfung
    if request.method != 'POST':
        return 'Methode nicht erlaubt', 405

    return handle_sensitive_action(request.get_json())

# Korrigiert: Vollständiger Objektvergleich
class User:
    def __init__(self, id, name, role):
        self.id = id
        self.name = name
        self.role = role

    def __eq__(self, other):
        # Korrigiert: Alle relevanten Attribute vergleichen
        if not isinstance(other, User):
            return False
        return (self.id == other.id and
                self.name == other.name and
                self.role == other.role)

# Korrigiert: Token-Typ-Unterscheidung
def secure_verify_token(token, expected_type='access'):
    decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])

    # Korrigiert: Token-Typ prüfen
    token_type = decoded.get('type')
    if token_type != expected_type:
        raise ValueError(f"Erwartetes {expected_type}-Token, bekam {token_type}")

    return decoded.get('user_id')

Die Korrektur implementiert explizite Typprüfung, inhaltsbasierte Validierung und ordnungsgemäße Typunterscheidung für Sicherheitsentscheidungen.


Ausgenutzt in der Praxis

Browser-Ereignis-Spoofing (CVE-2005-2260)

Browser-Benutzeroberfläche unterschied nicht zwischen benutzer-initiierten und synthetischen Ereignissen, was Skripten ermöglichte, Aktionen auszulösen, die scheinbar vom Benutzer stammten.

ACL-Verlust durch Typverwirrung (CVE-2005-2801)

Produkt verglich nicht alle erforderlichen Daten in separaten Elementen, behandelte sie fälschlicherweise als identisch und verursachte Verlust von Zugriffskontrolllisten.


Tools zum Testen/Ausnutzen

  • Burp Suite — Testen auf Typverwirrung in Webanwendungen.

  • Browser DevTools — Ereignisbehandlung und Typprüfung testen.

  • file-Befehl — Tatsächliche Dateitypen vs Erweiterungen analysieren.


CVE-Beispiele


Referenzen

  1. MITRE Corporation. "CWE-351: Insufficient Type Distinction." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/351.html

  2. MDN Web Docs. "Event.isTrusted." https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted

  3. OWASP Foundation. "Input Validation Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html