Funktionsaufruf mit falsch spezifizierten Argumenten

Beschreibung

Funktionsaufruf mit falsch spezifizierten Argumenten tritt auf, wenn eine Funktion mit Argumenten aufgerufen wird, die nicht den erwarteten Parametern in Typ, Reihenfolge oder Bedeutung entsprechen. Dies umfasst das Übergeben von Argumenten in falscher Reihenfolge, Verwendung falscher Einheiten (Sekunden vs. Millisekunden), fehlerhafte Formate oder semantisches Missverständnis dessen, was ein Parameter erwartet. Solche Fehler können zu Sicherheitsschwachstellen, Datenkorruption oder unerwartetem Verhalten führen.

Risiko

Sicherheitsfunktionen können stillschweigend fehlschlagen, wenn falsche Parameter übergeben werden. Kryptographische Operationen mit falschen Schlüsselgrößen werden geschwächt. Zugriffskontrollprüfungen können passieren, wenn sie fehlschlagen sollten. Buffer-Operationen mit falschen Größen verursachen Überläufe. Zeitbasierte Sicherheit (Tokens, Sessions) scheitert mit falschen Einheiten. Die Anwendung scheint zu funktionieren, während die Sicherheit kompromittiert ist.

Lösung

Verwenden Sie stark typisierte Funktionssignaturen. Aktivieren Sie Strict-Mode und Type-Checking. Verwenden Sie benannte Parameter, wo verfügbar. Dokumentieren Sie Parameteranforderungen klar. Implementieren Sie Parametervalidierung am Funktionseintritt. Verwenden Sie Code-Review und statische Analyse. Schreiben Sie umfassende Unit-Tests für Randfälle.

Häufige Auswirkungen

AuswirkungDetails
SicherheitBereich: Geschwächte Kontrollen

Sicherheitsfunktionen schützen nicht richtig.
IntegritätBereich: Datenkorruption

Falsche Parameter korrumpieren Daten oder Zustand.
VerfügbarkeitBereich: Abstürze

Typfehlanpassungen verursachen Laufzeitfehler.

Beispielcode + Lösungscode

Verwundbarer Code

// VERWUNDBAR: Falsche Argumentreihenfolge
void copyData(char *dest, const char *src, size_t size);

void vulnerableUsage() {
    char buffer[100];
    char *data = getData();

    // Argumente in falscher Reihenfolge! (src, dest, size)
    copyData(data, buffer, sizeof(buffer));  // Falsch!
}

// VERWUNDBAR: Falscher Größenparameter
void processBufferVulnerable(char *input) {
    char output[256];

    // Falsch: strlen vs sizeof
    memcpy(output, input, strlen(output));  // Verwendet uninitialisiertes output!

    // Falsch: Größe von Pointer vs Größe von Buffer
    char *buf = malloc(1024);
    memset(buf, 0, sizeof(buf));  // Löscht nur 8 Bytes (Zeigergröße)!
}

// VERWUNDBAR: Falsche Einheiten (Sekunden vs Millisekunden)
void setTimeoutVulnerable() {
    // API erwartet Millisekunden
    setTimeout(callback, 60);  // Beabsichtigt 60 Sekunden, bekam 60 Millisekunden!

    // API erwartet Sekunden aber bekommt Millisekunden
    setSessionExpiry(3600000);  // Beabsichtigt 1 Stunde, bekam 3,6 Millionen Sekunden!
}

// VERWUNDBAR: Typzwangs-Probleme
void handleUserVulnerable(int userId) {
    // Prüfen ob Benutzer Admin ist (Admin-ID ist 1)
    if (userId = 1) {  // Zuweisung, nicht Vergleich!
        grantAdminAccess();
    }
}
// VERWUNDBAR: Falsche Verschlüsselungsparameter
public class VulnerableCrypto {

    public byte[] encrypt(byte[] data, byte[] key) throws Exception {
        // Falsch: key und IV vertauscht
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        // Verwendet key als IV - Sicherheitsschwäche!
        IvParameterSpec iv = new IvParameterSpec(key);  // Sollte zufälliges IV sein!
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

        return cipher.doFinal(data);
    }

    // VERWUNDBAR: Falsche Argumentbedeutungen
    public void setPermissions(String resource, String user, int level) {
        // Aufrufer verwechselte Argumentreihenfolge
    }

    public void usePermissions() {
        // Falsche Reihenfolge: übergab level, user, resource
        setPermissions("admin", "document.txt", 5);  // user und resource vertauscht!
    }
}

// VERWUNDBAR: Substring mit falschen Indizes
public class StringHandler {
    public String extractVulnerable(String input, int start, int end) {
        // Falsch: end ist exklusiv in substring aber Aufrufer denkt inklusiv
        return input.substring(start, end);
    }

    public void usage() {
        String data = "SECRET_VALUE";
        // Will Zeichen 0-5 inklusiv, bekommt aber 0-4
        String result = extractVulnerable(data, 0, 5);  // Bekommt "SECRE" nicht "SECRET"
    }
}
# VERWUNDBAR: Veränderliches Standardargument
def append_item_vulnerable(item, target_list=[]):
    # Veränderliches Standard wird über Aufrufe geteilt!
    target_list.append(item)
    return target_list

result1 = append_item_vulnerable(1)  # [1]
result2 = append_item_vulnerable(2)  # [1, 2] - unerwartet!

# VERWUNDBAR: Falsche Positionsargumente
def create_user(username, password, is_admin=False):
    # Benutzer mit gegebenen Properties erstellen
    pass

# Aufrufer vertauschte Argumente
create_user("admin123", "john_doe")  # password als username verwendet!

# VERWUNDBAR: Format-String-Probleme
def log_message_vulnerable(message, level):
    # Format-String erwartet (level, message)
    print(f"[{message}] {level}")  # Vertauscht!

# VERWUNDBAR: Zeiteinheiten-Verwirrung
import time

def rate_limit_vulnerable(requests_per_second):
    # Sleep erwartet Sekunden, aber Aufrufer könnte Millisekunden übergeben
    time.sleep(1 / requests_per_second)

rate_limit_vulnerable(1000)  # Beabsichtigt 1000 req/sec, schläft 0,001 Sekunden

# VERWUNDBAR: Boolean-Argument-Verwirrung
def search_users(query, case_sensitive, include_deleted):
    pass

# Was bedeutet True, False ohne Kontext?
search_users("admin", True, False)
// VERWUNDBAR: Callback-Argumentreihenfolge
function fetchData(url, errorCallback, successCallback) {
    // Übliches Muster hat success zuerst, error danach
}

// Aufrufer erwartet (url, success, error)
fetchData('/api/data',
    (data) => console.log(data),      // Das ist tatsächlich errorCallback!
    (err) => console.error(err)       // Das ist tatsächlich successCallback!
);

// VERWUNDBAR: setTimeout-Einheiten
setTimeout(() => {
    refreshToken();
}, 60);  // 60 Millisekunden, nicht 60 Sekunden!

// VERWUNDBAR: Array-Methoden-Verwirrung
const users = ['admin', 'user', 'guest'];

// splice vs slice Verwirrung
const removed = users.slice(1, 1);  // Gibt leeres Array zurück, entfernt nicht
// Wollte: users.splice(1, 1) um Element an Index 1 zu entfernen

// VERWUNDBAR: Vergleich vs Zuweisung
function checkAdmin(user) {
    if (user.role = 'admin') {  // Zuweisung!
        return true;
    }
    return false;
}

Lösungscode

// SICHER: Klare Parameternamen und Validierung
typedef struct {
    char *dest;
    const char *src;
    size_t dest_size;
    size_t src_size;
} CopyParams;

int copyDataSafe(CopyParams *params) {
    if (!params || !params->dest || !params->src) {
        return -1;
    }

    size_t copy_size = params->src_size < params->dest_size
                       ? params->src_size
                       : params->dest_size - 1;

    memcpy(params->dest, params->src, copy_size);
    params->dest[copy_size] = '\0';

    return 0;
}

// SICHER: Ordentliche Größenberechnungen
void processBufferSafe(const char *input, size_t input_len) {
    if (input_len > 255) {
        input_len = 255;
    }

    char output[256];
    memset(output, 0, sizeof(output));  // sizeof(output) ist 256
    memcpy(output, input, input_len);
}

// SICHER: Klare Zeiteinheiten in Benennung
void setTimeoutSeconds(void (*callback)(void), int seconds) {
    setTimeoutMillis(callback, seconds * 1000);
}

void setTimeoutMillis(void (*callback)(void), int millis) {
    // Implementierung
}

// SICHER: Vergleich, nicht Zuweisung
void handleUserSafe(int userId) {
    if (userId == 1) {  // Korrekter Vergleich
        grantAdminAccess();
    }
}
// SICHER: Builder-Pattern für komplexe Parameter
public class SafeCrypto {

    public static class EncryptionParams {
        private final byte[] data;
        private final byte[] key;
        private final byte[] iv;

        private EncryptionParams(Builder builder) {
            this.data = builder.data;
            this.key = builder.key;
            this.iv = builder.iv;
        }

        public static class Builder {
            private byte[] data;
            private byte[] key;
            private byte[] iv;

            public Builder data(byte[] data) {
                this.data = data;
                return this;
            }

            public Builder key(byte[] key) {
                if (key.length != 16 && key.length != 24 && key.length != 32) {
                    throw new IllegalArgumentException("Ungültige Schlüsselgröße");
                }
                this.key = key;
                return this;
            }

            public Builder iv(byte[] iv) {
                if (iv.length != 16) {
                    throw new IllegalArgumentException("IV muss 16 Bytes sein");
                }
                this.iv = iv;
                return this;
            }

            public EncryptionParams build() {
                if (data == null || key == null || iv == null) {
                    throw new IllegalStateException("Fehlende erforderliche Parameter");
                }
                return new EncryptionParams(this);
            }
        }
    }

    public byte[] encrypt(EncryptionParams params) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(params.key, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(params.iv);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        return cipher.doFinal(params.data);
    }
}

// Verwendung - klar und typsicher
byte[] encrypted = crypto.encrypt(
    new SafeCrypto.EncryptionParams.Builder()
        .data(plaintext)
        .key(secretKey)
        .iv(secureRandom.generateSeed(16))
        .build()
);
from typing import Optional, List
from dataclasses import dataclass
from enum import Enum

# SICHER: Unveränderliches Standard, Typhinweise
def append_item_safe(item: str, target_list: Optional[List[str]] = None) -> List[str]:
    if target_list is None:
        target_list = []
    target_list.append(item)
    return target_list

# SICHER: Benannte Parameter und Dataclass
@dataclass
class UserCreationParams:
    username: str
    password: str
    is_admin: bool = False

def create_user_safe(params: UserCreationParams) -> None:
    # Klar was jedes Feld bedeutet
    pass

# Verwendung
create_user_safe(UserCreationParams(
    username="john_doe",
    password="secure123",
    is_admin=False
))

# SICHER: Explizite Zeiteinheiten
class TimeUnit(Enum):
    SECONDS = 1
    MILLISECONDS = 1000
    MINUTES = 1/60

def rate_limit_safe(requests_per: int, unit: TimeUnit) -> None:
    seconds = 1 / (requests_per * unit.value)
    time.sleep(seconds)

rate_limit_safe(1000, TimeUnit.SECONDS)  # Klar: 1000 pro Sekunde

# SICHER: Boolean-Argumente mit klaren Namen
def search_users_safe(
    query: str,
    *,  # Erzwingt Keyword-Argumente
    case_sensitive: bool = True,
    include_deleted: bool = False
) -> List[str]:
    pass

# Muss benannte Argumente verwenden - viel klarer
search_users_safe("admin", case_sensitive=True, include_deleted=False)
// SICHER: Options-Objekt-Pattern
function fetchDataSafe(url, options) {
    const {
        onSuccess = () => {},
        onError = () => {},
        timeout = 30000
    } = options;

    // Klar welcher Callback welcher ist
    fetch(url)
        .then(onSuccess)
        .catch(onError);
}

// Verwendung - klare Benennung
fetchDataSafe('/api/data', {
    onSuccess: (data) => console.log(data),
    onError: (err) => console.error(err)
});

// SICHER: Konstanten für Zeitwerte
const SECONDS = 1000;
const MINUTES = 60 * SECONDS;

setTimeout(() => {
    refreshToken();
}, 60 * SECONDS);  // Klar: 60 Sekunden

// SICHER: Expliziter Vergleich
function checkAdminSafe(user) {
    if (user.role === 'admin') {  // Strikte Gleichheit
        return true;
    }
    return false;
}

// SICHER: TypeScript für Typsicherheit
interface SearchOptions {
    query: string;
    caseSensitive?: boolean;
    includeDeleted?: boolean;
}

function searchUsersSafe(options: SearchOptions): User[] {
    const { query, caseSensitive = true, includeDeleted = false } = options;
    // Implementierung
}

Ausgenutzt in der Praxis

Kryptographische Schwächen

Falsche IV-/Schlüsselparameter schwächten Verschlüsselung.

Buffer-Überläufe

Falsche Größenargumente verursachten Speicherkorruption.

Authentifizierungs-Bypass

Vertauschte Credential-Parameter ermöglichten unbefugten Zugriff.


Tools zum Testen/Ausnutzen

  • Statische Analyse (ESLint, Pylint, clang-tidy).
  • Typ-Checker (TypeScript, mypy, flow).
  • Unit-Testing mit Randfällen.

CVE-Beispiele

  • Buffer-Überläufe durch falsche Größenparameter.
  • Krypto-Schwächen durch Parameterverwirrung.

Referenzen

  1. MITRE. "CWE-628: Function Call with Incorrectly Specified Arguments." https://cwe.mitre.org/data/definitions/628.html
  2. Code-Review-Best-Practices-Dokumentation.