Verwendung von Funktionen mit inkonsistenten Implementierungen

Beschreibung

Die Verwendung von Funktionen mit inkonsistenten Implementierungen ist eine Schwachstelle, bei der Code eine Funktion verwendet, die sich zwischen Betriebssystemen, Compilern oder Bibliotheksversionen unterschiedlich verhält. Diese Implementierungsvariationen können Sicherheitslücken erzeugen, wenn Code auf neue Plattformen portiert oder in unerwarteten Umgebungen kompiliert wird. Die Inkonsistenzen können sich als unterschiedliche Parameterinterpretation, variierende Sicherheitsrisiken, plattformspezifische Verfügbarkeit oder geänderte Bedeutungen von Rückgabecodes manifestieren.

Risiko

Funktionen mit inkonsistenten Implementierungen erzeugen unvorhersehbare Sicherheitsverhaltensweisen. Code, der auf einer Plattform sicher funktioniert, kann auf einer anderen Schwachstellen haben. Sicherheitskritische String-Funktionen können unterschiedliche Pufferbehandlung zwischen Plattformen haben. Kryptographische Funktionen können unterschiedliche Standardwerte oder Algorithmen verwenden. Zeit-Funktionen können unterschiedliche Präzision oder Überlaufverhalten haben. Das Risiko ist verstärkt, weil diese Probleme oft nur in Produktionsumgebungen auftreten, die sich von der Entwicklung unterscheiden, was sie während des Testens schwer erkennbar macht.

Lösung

Identifizieren und lehnen Sie während der Architektur- und Designphasen APIs ab, die inkonsistentes Verhalten zeigen, wenn solche Abweichungen das Risikoniveau erhöhen. Verwenden Sie gut dokumentierte, standardisierte APIs mit konsistentem Verhalten über Zielplattformen hinweg. Erstellen Sie Abstraktionsschichten, die plattformspezifisches Verhalten normalisieren. Dokumentieren Sie alle Plattformannahmen und testen Sie auf allen Deployment-Zielen. Verwenden Sie statische Analysewerkzeuge, um die Verwendung von Funktionen mit bekannten Implementierungsinkonsistenzen zu erkennen. Erwägen Sie die Verwendung portabler Bibliotheken, die konsistente Implementierungen über Plattformen hinweg bieten.

Häufige Auswirkungen

AuswirkungDetails
AndereUmfang: Ändere

Qualitätsverschlechterung - Code kann sich über Plattformen hinweg inkonsistent verhalten, was zu schwer reproduzierbaren Bugs und Sicherheitsproblemen führt.
AndereUmfang: Ändere

Variiert nach Kontext - Sicherheitsimplikationen hängen davon ab, welche Funktion verwendet wird und wie sich ihr Verhalten zwischen Implementierungen unterscheidet.

Beispielcode

Anfälliger Code

// Anfällig: strcpy/strncpy haben inkonsistentes Null-Terminierungsverhalten
#include <string.h>

void vulnerable_string_copy(char *dest, const char *src, size_t dest_size) {
    // Anfällig: strncpy-Verhalten variiert:
    // - Kann null-terminieren oder nicht
    // - Kann verbleibenden Puffer mit Nullen füllen oder nicht
    // - Unterschiedliches Verhalten bei überlappenden Puffern

    strncpy(dest, src, dest_size);

    // Auf einigen Implementierungen ist dest NICHT null-terminiert wenn src >= dest_size
    // Auf anderen ist es immer null-terminiert
    // Code der ein Verhalten annimmt bricht auf anderen Plattformen

    printf("Kopiert: %s\n", dest);  // Kann über Puffer hinaus lesen
}

// Anfällig: signal()-Verhalten variiert erheblich
#include <signal.h>

void vulnerable_signal_handler() {
    // Anfällig: signal()-Verhalten unterscheidet sich:
    // - Auf System V: Signal-Handler wird nach erster Aufrufung auf SIG_DFL zurückgesetzt
    // - Auf BSD: Signal-Handler bleibt installiert
    // - Auf Linux: hängt von Konfiguration und Flags ab

    signal(SIGINT, handle_interrupt);

    // Code der BSD-Semantik erwartet bricht auf System V
    // SIGINT-Handler funktioniert möglicherweise nur einmal, dann Programmabsturz
}

// Anfällig: printf-Format-Spezifizierer variieren
void vulnerable_format_output(long value) {
    // Anfällig: %ld-Verhalten auf verschiedenen Systemen
    // - Größe von 'long' variiert (32 oder 64 Bits)
    // - Einige Systeme erfordern %lld für 64-Bit

    printf("Wert: %ld\n", value);  // Kann auf einigen Plattformen abschneiden

    // Anfällig: printf-Rückgabewert-Behandlung
    // Einige Implementierungen geben -1 für Fehler zurück
    // Andere können Teil-Zählungen oder verschiedene Werte zurückgeben
}
<?php
// Anfällig: PHP array_merge-Verhalten hat sich zwischen Versionen geändert
function vulnerable_config_merge($default_config, $user_config) {
    // Anfällig: array_merge-Verhalten unterscheidet sich:
    // - PHP 5.x vs 7.x: unterschiedliche Behandlung von Integer-Schlüsseln
    // - Verschiedene Versionen behandeln Null-Werte unterschiedlich

    $config = array_merge($default_config, $user_config);

    // In einigen Versionen werden Integer-Schlüssel neu nummeriert
    // In anderen werden sie beibehalten
    // Sicherheitsrelevante Konfiguration kann falsch sein
    return $config;
}

// Anfällig: crypt() hat plattformabhängiges Verhalten
function vulnerable_password_hash($password) {
    // Anfällig: crypt()-Algorithmusauswahl variiert nach Plattform
    // - Linux: kann bcrypt, SHA-256, SHA-512 unterstützen
    // - Andere Systeme: unterstützen möglicherweise nur DES (schwach)
    // - Salt-Format-Interpretation variiert

    $hash = crypt($password, '$6$salt

```python
# Anfällig: OS-abhängige Pfadbehandlung
import os

def vulnerable_path_handling(user_path):
    # Anfällig: os.path.join-Verhalten unterscheidet sich:
    # - Windows: behandelt Laufwerksbuchstaben speziell
    # - Unix: behandelt alles als Pfadkomponenten
    # - Trailing-Slashes werden unterschiedlich behandelt

    base = "/var/data"
    full_path = os.path.join(base, user_path)

    # Auf Windows: "C:\\evil" in user_path ignoriert base komplett
    # Auf Unix: "../" Traversal kann unterschiedlich funktionieren
    return full_path

# Anfällig: tempfile-Verhalten variiert
import tempfile

def vulnerable_temp_file():
    # Anfällig: tempfile-Verhalten unterscheidet sich:
    # - Standardverzeichnis variiert nach OS
    # - Berechtigungsbehandlung unterscheidet sich
    # - Einige Plattformen können vorhersagbare Namen verwenden

    fd, path = tempfile.mkstemp()

    # Auf einigen Systemen können temp-Dateien für alle lesbar sein
    # Pfad-Vorhersagbarkeit variiert nach Implementierung
    return path
// Anfällig: Dateipfad-Behandlung variiert nach OS
public class VulnerableFilePaths {

    public File getConfigFile(String filename) {
        // Anfällig: Pfadtrenner variiert
        // - Windows: Backslash
        // - Unix: Schrägstrich
        // File.separator hilft aber Grenzfälle bleiben

        String path = "config/" + filename;  // Schrägstrich

        // Auf Windows kann dies nicht wie erwartet auflösen
        // Gemischte Trenner werden inkonsistent behandelt
        return new File(path);
    }

    // Anfällig: String.format ist locale-abhängig
    public String formatNumber(double value) {
        // Anfällig: Format-Verhalten variiert nach Locale
        // - Dezimaltrennzeichen: . vs ,
        // - Gruppierungstrennzeichen: , vs . vs Leerzeichen
        // - Anzahl der Dezimalstellen

        return String.format("%.2f", value);

        // "1234.56" in US-Locale
        // "1234,56" in deutscher Locale
        // Kann Parsing oder Sicherheitsvergleiche brechen
    }
}

Korrigierter Code

// Korrigiert: Konsistente, wohldefinierte String-Funktionen verwenden
#include <string.h>
#include <stdio.h>

void secure_string_copy(char *dest, const char *src, size_t dest_size) {
    if (dest_size == 0) return;

    // Korrigiert: strlcpy verwenden welches konsistentes Verhalten hat
    // Oder explizit implementieren um Konsistenz sicherzustellen
    size_t src_len = strlen(src);
    size_t copy_len = (src_len < dest_size - 1) ? src_len : dest_size - 1;

    memcpy(dest, src, copy_len);
    dest[copy_len] = '\0';  // Korrigiert: Immer null-terminieren

    // Oder snprintf verwenden welches konsistenteres Verhalten hat
    snprintf(dest, dest_size, "%s", src);
}

// Korrigiert: sigaction anstelle von signal verwenden
#include <signal.h>

void secure_signal_handler() {
    // Korrigiert: sigaction hat konsistenteres, portables Verhalten
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = handle_interrupt;
    sa.sa_flags = SA_RESTART;  // Explizite Flags für konsistentes Verhalten
    sigemptyset(&sa.sa_mask);

    sigaction(SIGINT, &sa, NULL);

    // Verhalten ist jetzt konsistent über Plattformen hinweg
}

// Korrigiert: Explizite Format-Spezifizierer verwenden
#include <inttypes.h>

void secure_format_output(int64_t value) {
    // Korrigiert: Feste-Breite-Typen und ihre Format-Makros verwenden
    printf("Wert: %" PRId64 "\n", value);

    // Oder explizit dimensionierte Typen verwenden
    int32_t val32 = (int32_t)value;
    printf("32-bit: %" PRId32 "\n", val32);
}
<?php
// Korrigiert: Explizites Merge mit definiertem Verhalten
function secure_config_merge($default_config, $user_config) {
    // Korrigiert: Explizites Merge-Verhalten implementieren
    $config = [];

    // Defaults kopieren
    foreach ($default_config as $key => $value) {
        $config[$key] = $value;
    }

    // Mit Benutzerkonfiguration überschreiben
    foreach ($user_config as $key => $value) {
        // Korrigiert: Explizite Schlüsselbehandlung
        if (is_string($key)) {
            $config[$key] = $value;
        }
        // Integer-Schlüssel aus Benutzerkonfiguration für Sicherheit ignorieren
    }

    return $config;
}

// Korrigiert: password_hash mit explizitem Algorithmus verwenden
function secure_password_hash($password) {
    // Korrigiert: password_hash mit explizitem, konsistentem Algorithmus verwenden
    $options = [
        'cost' => 12,  // Expliziter Kostenfaktor
    ];

    // Korrigiert: PASSWORD_BCRYPT hat konsistentes Verhalten über PHP-Versionen
    $hash = password_hash($password, PASSWORD_BCRYPT, $options);

    // Oder PASSWORD_ARGON2ID auf PHP 7.3+ verwenden
    // $hash = password_hash($password, PASSWORD_ARGON2ID);

    return $hash;
}
?>
# Korrigiert: Plattform-konsistente Pfadbehandlung
import os
from pathlib import Path

def secure_path_handling(user_path):
    # Korrigiert: pathlib für konsistentes Verhalten verwenden
    base = Path("/var/data")

    # Korrigiert: Pfad auflösen und validieren
    try:
        user_part = Path(user_path)

        # Korrigiert: Absolute Pfade in Benutzereingabe ablehnen
        if user_part.is_absolute():
            raise ValueError("Absolute Pfade nicht erlaubt")

        # Korrigiert: Auflösen und Enthaltensein prüfen
        full_path = (base / user_part).resolve()

        if not str(full_path).startswith(str(base.resolve())):
            raise ValueError("Path Traversal erkannt")

        return str(full_path)

    except (ValueError, OSError) as e:
        raise SecurityException(f"Ungültiger Pfad: {e}")

# Korrigiert: Konsistente Temp-Datei-Behandlung
import tempfile
import os

def secure_temp_file():
    # Korrigiert: Explizites Verzeichnis und Berechtigungen
    temp_dir = os.environ.get('SECURE_TEMP', '/var/tmp/secure')
    os.makedirs(temp_dir, mode=0o700, exist_ok=True)

    # Korrigiert: Mit expliziten Berechtigungen erstellen
    fd, path = tempfile.mkstemp(dir=temp_dir)

    # Korrigiert: Restriktive Berechtigungen setzen
    os.chmod(path, 0o600)

    return fd, path
// Korrigiert: Plattform-unabhängige Dateibehandlung
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.NumberFormat;
import java.util.Locale;

public class SecureFilePaths {

    public Path getConfigFile(String filename) {
        // Korrigiert: Paths.get für plattform-unabhängige Behandlung verwenden
        Path configDir = Paths.get("config");

        // Korrigiert: Dateiname validieren
        if (filename.contains("..") ||
            filename.contains("/") ||
            filename.contains("\\")) {
            throw new SecurityException("Ungültiger Dateiname");
        }

        return configDir.resolve(filename);
    }

    // Korrigiert: Explizite Locale für Zahlenformatierung
    public String formatNumber(double value) {
        // Korrigiert: Explizite Locale für konsistentes Verhalten verwenden
        NumberFormat formatter = NumberFormat.getInstance(Locale.US);
        formatter.setMinimumFractionDigits(2);
        formatter.setMaximumFractionDigits(2);

        return formatter.format(value);
    }

    // Korrigiert: Binäres Format für maschinenlesbare Daten
    public String formatMachineReadable(double value) {
        // Korrigiert: Locale-unabhängiges Format für Datenaustausch verwenden
        return String.format(Locale.ROOT, "%.2f", value);
    }
}

CVE-Beispiele

Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE aufgelistet. Das Muster ist jedoch dokumentiert in:

  • Seven Pernicious Kingdoms Taxonomie als "Inconsistent Implementations"
  • Cross-Platform-Sicherheitshinweise für libc-Funktions-Verhaltensunterschiede

Referenzen

  1. MITRE Corporation. "CWE-474: Use of Function with Inconsistent Implementations." https://cwe.mitre.org/data/definitions/474.html
  2. CERT C Secure Coding Standard. "MSC14-C. Do not introduce unnecessary platform dependencies."