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
| Auswirkung | Details |
|---|---|
| Andere | Umfang: Ändere Qualitätsverschlechterung - Code kann sich über Plattformen hinweg inkonsistent verhalten, was zu schwer reproduzierbaren Bugs und Sicherheitsproblemen führt. |
| Andere | Umfang: Ä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
- MITRE Corporation. "CWE-474: Use of Function with Inconsistent Implementations." https://cwe.mitre.org/data/definitions/474.html
- CERT C Secure Coding Standard. "MSC14-C. Do not introduce unnecessary platform dependencies."