Aufruf einer nicht-ubiquitären API
Beschreibung
Aufruf einer nicht-ubiquitären API ist eine Schwachstelle, bei der ein Produkt eine API-Funktion verwendet, die nicht auf allen Versionen der Zielplattform oder des Betriebssystems existiert. Dies umfasst Funktionen, die in neueren Versionen hinzugefügt, in bestimmten Versionen veraltet oder entfernt wurden, oder nur auf bestimmten Plattformvarianten verfügbar sind. Wenn Code sich auf solche APIs ohne ordnungsgemäße Versionsprüfung oder Fallback-Mechanismen verlässt, kann die Anwendung auf Systemen, wo die API nicht verfügbar ist, nicht starten oder sich falsch verhalten. Dies ist besonders problematisch für sicherheitsbezogene Funktionen, die auf älteren Systemen fehlen können.
Risiko
Die Verwendung nicht-ubiquitärer APIs erzeugt erhebliche Bereitstellungs- und Sicherheitsrisiken. Anwendungen können abstürzen oder nicht starten auf Plattformen, denen die erforderlichen Funktionen fehlen. Sicherheitsfeatures, die von neueren APIs abhängen, können auf älteren Systemen stillschweigend fehlschlagen und Benutzer ungeschützt lassen. Funktionen, die aus Sicherheitsgründen veraltet sind, können noch verwendet werden und Systeme bekannten Schwachstellen aussetzen. Das Verhalten der Anwendung wird über verschiedene Plattformversionen inkonsistent, was Tests und Support erschwert. Angreifer können gezielt ältere Systeme angreifen, wo Sicherheits-APIs fehlen. Laufzeitfehler durch fehlende Funktionen bieten schlechte Benutzererfahrung und potenzielle Denial-of-Service.
Lösung
Identifizieren Sie die minimale Plattformversion, die Ihre Anwendung unterstützen muss, und dokumentieren Sie diese klar. Verwenden Sie Compile-Zeit-Prüfungen, um API-Verfügbarkeit zu verifizieren, wenn möglich. Implementieren Sie Laufzeit-Versionsprüfungen vor dem Aufruf von APIs, die möglicherweise nicht existieren. Stellen Sie Fallback-Implementierungen für kritische Funktionalität bereit, wenn primäre APIs nicht verfügbar sind. Testen Sie sowohl auf der ältesten als auch auf der neuesten unterstützten Plattformversion. Verwenden Sie Plattformabstraktionsschichten, die Versionsunterschiede intern behandeln. Vermeiden Sie veraltete Funktionen und planen Sie Migrationspfade, wenn APIs zur Entfernung vorgesehen sind.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sonstige | Bereich: Sonstige Qualitätsverschlechterung - Anwendung schlägt fehl oder verhält sich inkonsistent auf Plattformen, denen die erforderlichen API-Funktionen fehlen. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz, Beendigung oder Neustart - Aufruf nicht existierender Funktionen verursacht Laufzeitfehler und Anwendungsabstürze. |
Beispielcode
Verwundbarer Code
// Verwundbar: Verwendung von Windows Vista+ API ohne Versionsprüfung
#include <windows.h>
#include <bcrypt.h>
// Verwundbar: BCrypt-Funktionen nur ab Vista verfügbar
int vulnerable_generate_random(unsigned char* buffer, size_t length) {
NTSTATUS status;
// BCryptGenRandom existiert nicht auf Windows XP
status = BCryptGenRandom(NULL, buffer, length,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return NT_SUCCESS(status) ? 0 : -1;
// Stürzt auf Windows XP ab!
}
// Verwundbar: Verwendung von POSIX.1-2008 Funktion ohne Prüfung
#define _GNU_SOURCE
#include <string.h>
// Verwundbar: strnlen nicht auf allen Systemen verfügbar
size_t vulnerable_safe_strlen(const char* str, size_t maxlen) {
// strnlen ist POSIX.1-2008, nicht auf älteren Systemen verfügbar
return strnlen(str, maxlen);
// Kann Link-Fehler oder Laufzeitfehler auf alten Plattformen verursachen
}
// Verwundbar: Verwendung Linux-spezifischer Syscall
#include <sys/random.h>
int vulnerable_get_random(void* buf, size_t buflen) {
// getrandom() hinzugefügt in Linux 3.17, glibc 2.25
return getrandom(buf, buflen, 0);
// Schlägt auf älteren Kernels/glibc fehl
}
// Verwundbar: Verwendung Java-versionsspezifischer API
public class VulnerableJavaApi {
// Verwundbar: String.isBlank() nur in Java 11+
public boolean vulnerable_isEmpty(String s) {
return s.isBlank(); // NoSuchMethodError auf Java 8!
}
// Verwundbar: Files.readString() nur in Java 11+
public String vulnerable_readFile(Path path) throws IOException {
return Files.readString(path); // Existiert nicht in Java 8
}
// Verwundbar: Verwendung von Preview-Features
public void vulnerable_patternMatch(Object obj) {
// Pattern Matching nur in Java 14+ (preview) / 16+ (final)
if (obj instanceof String s) {
System.out.println(s.length());
}
}
}
// Verwundbar: Verwendung Android-API ohne Versionsprüfung
public class VulnerableAndroidApi {
// Verwundbar: API-Level nicht geprüft
public void vulnerable_createNotificationChannel(Context context) {
// NotificationChannel hinzugefügt in API 26 (Android 8.0)
NotificationChannel channel = new NotificationChannel(
"my_channel", "My Channel", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager nm = context.getSystemService(NotificationManager.class);
nm.createNotificationChannel(channel);
// Stürzt auf Android 7.1 und niedriger ab!
}
}
# Verwundbar: Verwendung Python-versionsspezifischer Features
import sys
# Verwundbar: f-strings nur in Python 3.6+
def vulnerable_format(name):
return f"Hello, {name}!" # SyntaxError auf Python 3.5
# Verwundbar: := Operator nur in Python 3.8+
def vulnerable_walrus():
if (n := len(some_list)) > 10: # SyntaxError auf Python 3.7
print(f"Liste zu lang: {n}")
# Verwundbar: Verwendung von Typing-Features aus neueren Versionen
from typing import TypedDict # Nur Python 3.8+
class Person(TypedDict): # Schlägt auf 3.7 fehl
name: str
age: int
Lösungscode
// Sicher: Laufzeit-Versionsprüfung für Windows-APIs
#include <windows.h>
#include <wincrypt.h>
// Funktionszeiger für dynamisches Laden
typedef NTSTATUS (WINAPI *PFN_BCryptGenRandom)(
BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG);
int secure_generate_random(unsigned char* buffer, size_t length) {
// Zuerst moderne API versuchen
HMODULE bcrypt = LoadLibraryA("bcrypt.dll");
if (bcrypt) {
PFN_BCryptGenRandom pfnBCryptGenRandom =
(PFN_BCryptGenRandom)GetProcAddress(bcrypt, "BCryptGenRandom");
if (pfnBCryptGenRandom) {
NTSTATUS status = pfnBCryptGenRandom(
NULL, buffer, (ULONG)length,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
FreeLibrary(bcrypt);
return NT_SUCCESS(status) ? 0 : -1;
}
FreeLibrary(bcrypt);
}
// Fallback zu CryptoAPI (Windows XP kompatibel)
HCRYPTPROV hProv;
if (!CryptAcquireContext(&hProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
return -1;
}
int result = CryptGenRandom(hProv, (DWORD)length, buffer) ? 0 : -1;
CryptReleaseContext(hProv, 0);
return result;
}
// Sicher: Compile-Zeit- und Laufzeitprüfungen für POSIX-Funktionen
#include <string.h>
size_t secure_safe_strlen(const char* str, size_t maxlen) {
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
// strnlen verfügbar
return strnlen(str, maxlen);
#else
// Fallback-Implementierung
size_t len = 0;
while (len < maxlen && str[len] != '\0') {
len++;
}
return len;
#endif
}
// Sicher: Linux-Versionsprüfung mit Fallback
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>
int secure_get_random(void* buf, size_t buflen) {
#if defined(__linux__)
// Zur Laufzeit auf getrandom prüfen
#ifdef SYS_getrandom
long ret = syscall(SYS_getrandom, buf, buflen, 0);
if (ret >= 0) {
return 0;
}
if (errno != ENOSYS) {
return -1; // Echter Fehler, nicht fehlender Syscall
}
#endif
// Fallback zu /dev/urandom
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
ssize_t result = read(fd, buf, buflen);
close(fd);
return (result == (ssize_t)buflen) ? 0 : -1;
#else
// Nicht-Linux: Plattformspezifische Methode verwenden
return platform_get_random(buf, buflen);
#endif
}
// Sicher: Java-versionsabhängiger Code
public class SecureJavaApi {
// Sicher: Versionsprüfung mit Fallback
public boolean secure_isEmpty(String s) {
if (s == null) return true;
// Java-Version zur Laufzeit prüfen
int version = getJavaVersion();
if (version >= 11) {
// Reflection verwenden um isBlank() aufzurufen wenn verfügbar
try {
return (Boolean) String.class
.getMethod("isBlank")
.invoke(s);
} catch (Exception e) {
// Zum Fallback durchfallen
}
}
// Fallback für Java 8
return s.trim().isEmpty();
}
private int getJavaVersion() {
String version = System.getProperty("java.version");
if (version.startsWith("1.")) {
return Integer.parseInt(version.substring(2, 3));
}
int dot = version.indexOf('.');
if (dot != -1) {
return Integer.parseInt(version.substring(0, dot));
}
return Integer.parseInt(version);
}
// Sicher: Kompatible API verwenden
public String secure_readFile(Path path) throws IOException {
// Funktioniert auf Java 8+
byte[] bytes = Files.readAllBytes(path);
return new String(bytes, StandardCharsets.UTF_8);
}
}
// Sicher: Android-API mit Versionsprüfung
public class SecureAndroidApi {
public void secure_createNotificationChannel(Context context) {
// Sicher: API-Level vor Verwendung neuer APIs prüfen
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"my_channel", "My Channel",
NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager nm = context.getSystemService(
NotificationManager.class);
nm.createNotificationChannel(channel);
}
// Für ältere Versionen werden Channels nicht benötigt
}
// AndroidX für Rückwärtskompatibilität verwenden
public void secure_showNotification(Context context) {
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, "my_channel")
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Titel")
.setContentText("Nachricht")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
// NotificationCompat behandelt Versionsunterschiede
NotificationManagerCompat nm = NotificationManagerCompat.from(context);
nm.notify(1, builder.build());
}
}
# Sicher: Python-Versionsprüfungen
import sys
# Sicher: Versionsprüfung für f-strings
def secure_format(name):
if sys.version_info >= (3, 6):
return f"Hello, {name}!"
else:
return "Hello, {}!".format(name)
# Sicher: Bedingte Imports mit Fallbacks
try:
from typing import TypedDict
except ImportError:
# Python < 3.8 Fallback
from typing import Dict
TypedDict = Dict # Vereinfachter Fallback
# Sicher: Feature-Erkennung
def secure_get_list_length():
some_list = [1, 2, 3, 4, 5]
# Walrus-Operator nur in 3.8+
if sys.version_info >= (3, 8):
# Kann Walrus hier nicht verwenden, da Syntaxfehler auf älteren Versionen
# exec() oder separates Modul für versionsspezifischen Code verwenden
pass
# Kompatibler Ansatz
n = len(some_list)
if n > 10:
print("Liste zu lang: {}".format(n))
# Sicher: Verwendung von Kompatibilitätsbibliotheken
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import TypedDict
# Laufzeitkompatibilität
try:
from typing import TypedDict
except ImportError:
class TypedDict(dict):
"""Fallback TypedDict für Python < 3.8"""
def __init_subclass__(cls, **kwargs):
pass
CVE-Beispiele
Keine spezifischen CVEs werden dieser CWE direkt zugeordnet, obwohl viele Schwachstellen aus fehlenden Sicherheitsfunktionen auf älteren Plattformen entstehen.
Referenzen
- MITRE Corporation. "CWE-589: Call to Non-ubiquitous API." https://cwe.mitre.org/data/definitions/589.html
- Microsoft. "Version Helper Functions."
- Android Developers. "Build.VERSION_CODES."