Operator-Präzedenz-Logikfehler

Beschreibung

Operator-Präzedenz-Logikfehler ist eine Programmierschwachstelle, bei der ein Ausdruck Operator-Präzedenz auf eine Weise verwendet, die inkorrekte Logik erzeugt. Verschiedene Operatoren haben unterschiedliche Präzedenz-Ebenen (Auswertungsreihenfolge), und wenn Entwickler dies nicht berücksichtigen, können Ausdrücke anders als beabsichtigt ausgewertet werden. Dies ist besonders gefährlich in sicherheitskritischem Code wie Authentifizierungsentscheidungen, Zugriffskontrollprüfungen oder Finanzberechnungen, wo inkorrekte Logik zu Sicherheitsumgehungen oder Datenbeschädigung führen kann.

Risiko

Operator-Präzedenz-Fehler können schwerwiegende Sicherheitsimplikationen haben, je nach Kontext. In Authentifizierungscode kann inkorrekte Präzedenz dazu führen, dass Authentifizierung immer erfolgreich ist oder immer fehlschlägt. In Zugriffskontrolllogik kann es unbefugten Zugriff gewähren oder legitime Benutzer verweigern. In Finanzberechnungen kann inkorrekte Präzedenz zu falschen Beträgen führen. Die Bugs sind subtil und werden möglicherweise in grundlegenden Tests nicht erkannt, weil sie für einige Eingaben korrekte Ergebnisse liefern, während sie für andere versagen.

Lösung

Verwenden Sie Klammern großzügig, um die Auswertungsreihenfolge von Ausdrücken explizit zu machen, insbesondere in sicherheitskritischem Code. Verlassen Sie sich nicht auf Erinnerung an Operator-Präzedenz-Regeln - machen Sie die Absicht durch Klammern deutlich. Während Code-Reviews achten Sie besonders auf Ausdrücke, die Zuweisung, Vergleich und logische Operatoren kombinieren. Verwenden Sie Compiler-Warnungen (wie -Wparentheses in GCC), um verdächtige Ausdrücke zu erkennen. Schreiben Sie Unit-Tests, die speziell Randfälle in booleschen Ausdrücken ansprechen.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Unerwarteter Zustand - Ausdruck wird zu unbeabsichtigtem Wert ausgewertet und erzeugt inkorrektes Programmverhalten.
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Präzedenz-Fehler in Authentifizierungs- oder Autorisierungslogik können Sicherheitsprüfungen umgehen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz - Unerwartete Werte können Abstürze oder undefiniertes Verhalten verursachen.

Beispielcode

Verwundbarer Code

// Verwundbar: Zuweisung vs Vergleich Präzedenz
#include <stdio.h>

#define FAIL 0
#define SUCCESS 1

int AuthenticateUser(const char* username, const char* password) {
    // Gibt SUCCESS oder FAIL zurück
    return checkCredentials(username, password);
}

int vulnerable_login(const char* username, const char* password) {
    int isUser;

    // Verwundbar: == hat höhere Präzedenz als =
    // Wird ausgewertet als: isUser = (AuthenticateUser(...) == FAIL)
    // Anstatt von: (isUser = AuthenticateUser(...)) == FAIL
    if (isUser = AuthenticateUser(username, password) == FAIL) {
        // Dieser Zweig wird ausgeführt wenn Auth ERFOLGREICH ist (== FAIL ist false, also isUser = 0)
        return FAIL;  // Falsch! Lehnt authentifizierte Benutzer ab
    }

    // Erreicht hier wenn Auth Fehlschlägt (== FAIL ist true, also isUser = 1)
    return SUCCESS;  // Falsch! Erfolgreich für fehlgeschlagene Auth
}
// Verwundbar: Finanzberechnung mit falscher Präzedenz
public class VulnerableFinance {

    public double vulnerableROI(double currentValue, double initialInvestment) {
        // Verwundbar: Division vor Subtraktion
        // Wird ausgewertet als: currentValue - (initialInvestment / initialInvestment)
        // Was ist: currentValue - 1.0
        double returnROI = currentValue - initialInvestment / initialInvestment;

        // Wenn currentValue=150, initialInvestment=100
        // Erwartet: (150-100)/100 = 0.5 (50% ROI)
        // Tatsächlich: 150 - (100/100) = 150 - 1 = 149 (komplett falsch!)
        return returnROI;
    }
}
# Verwundbar: Python-spezifische Präzedenz-Probleme
def vulnerable_check(value, flag):
    # Verwundbar: 'not' gilt nur für ersten Operanden
    # Liest sich als: (not value) in [1, 2, 3]
    # Anstatt von: not (value in [1, 2, 3])
    if not value in [1, 2, 3] and flag:
        return True
    return False

Gefixter Code

// Gefixt: Explizite Klammern für Authentifizierung
#include <stdio.h>

#define FAIL 0
#define SUCCESS 1

int fixed_login(const char* username, const char* password) {
    int isUser;

    // Gefixt: Klammern machen Auswertungsreihenfolge explizit
    if ((isUser = AuthenticateUser(username, password)) == FAIL) {
        // Jetzt korrekt in diesen Zweig wenn Auth fehlschlägt
        return FAIL;
    }

    // Erreicht hier korrekt nur wenn Auth erfolgreich ist
    return SUCCESS;
}

// Noch besser: Zuweisung von Vergleich trennen
int fixed_login_v2(const char* username, const char* password) {
    // Gefixt: Klarer, eindeutiger Code
    int authResult = AuthenticateUser(username, password);

    if (authResult == FAIL) {
        return FAIL;
    }

    return SUCCESS;
}
// Gefixt: Finanzberechnung mit korrekter Präzedenz
public class FixedFinance {

    public double fixedROI(double currentValue, double initialInvestment) {
        // Gefixt: Klammern stellen korrekte Reihenfolge sicher
        double returnROI = (currentValue - initialInvestment) / initialInvestment;

        // Wenn currentValue=150, initialInvestment=100
        // Korrekt: (150-100)/100 = 0.5 (50% ROI)
        return returnROI;
    }

    // Noch besser: Zwischenvariablen verwenden
    public double fixedROI_v2(double currentValue, double initialInvestment) {
        double profit = currentValue - initialInvestment;
        double returnROI = profit / initialInvestment;
        return returnROI;
    }
}
# Gefixt: Python mit expliziten Klammern
def fixed_check(value, flag):
    # Gefixt: Klammern verdeutlichen 'not'-Geltungsbereich
    if (not (value in [1, 2, 3])) and flag:
        return True
    # Oder noch klarer:
    if value not in [1, 2, 3] and flag:  # 'not in'-Operator verwenden
        return True
    return False

CVE-Beispiele

  • CVE-2008-2516: Authentifizierungsumgehung durch Operator-Präzedenz-Fehler in Bedingung.
  • CVE-2008-0599: Puffergrößen-Fehlberechnung durch inkorrekte Operator-Präzedenz.
  • CVE-2001-1155: DNS-Verifizierungsumgehung resultierend aus Präzedenz-Fehler.

Referenzen

  1. MITRE Corporation. "CWE-783: Operator Precedence Logic Error." https://cwe.mitre.org/data/definitions/783.html
  2. CERT C Coding Standard. "EXP00-C. Use parentheses for precedence of operation."
  3. C++ Core Guidelines. "ES.41: If in doubt about operator precedence, parenthesize."