Beobachtbare Timing-Diskrepanz

Beschreibung

Beobachtbare Timing-Diskrepanz ist eine Schwachstelle, die auftritt, wenn zwei oder mehr Operationen in einem Produkt unterschiedliche Zeitmengen zur Fertigstellung benötigen, und diese Zeitunterschiede für einen Akteur beobachtbar sind und sicherheitsrelevante Informationen über den Systemzustand offenlegen. Diese Schwäche ist besonders kritisch in kryptografischen Kontexten, wo Angreifer Eigenschaften geheimer Schlüssel ableiten können, indem sie die für kryptografische Operationen benötigte Zeit messen. Häufige Manifestationen umfassen String-Vergleiche, die bei Finden einer Nichtübereinstimmung vorzeitig beenden, Authentifizierungsprüfungen, die für gültige Benutzer länger dauern, und kryptografische Operationen, deren Timing basierend auf Schlüsselbits variiert. Selbst Unterschiede im Mikrosekundenbereich können durch statistische Analyse mehrerer Messungen ausgenutzt werden, um vollständige Geheimnisse zu extrahieren.

Risiko

Beobachtbare Timing-Diskrepanzen stellen einen der subtilsten, aber mächtigsten Angriffsvektoren gegen sichere Systeme dar. In kryptografischen Implementierungen können Zeitvariationen während Operationen wie modularer Exponentiation oder Vergleich komplette private Schlüssel durch statistische Analyse preisgeben. Authentifizierungssysteme, die Passwörter Zeichen für Zeichen validieren, ermöglichen Angreifern, korrekte Anmeldedaten ein Zeichen nach dem anderen zu bestimmen, was den zu durchsuchenden Schlüsselraum dramatisch reduziert. Remote-Timing-Angriffe über Netzwerke bleiben trotz Rauschen machbar, da Angreifer Messungen über Tausende von Anfragen aggregieren können, um Mikrosekunden-Präzision zu erreichen. Das Risiko wird in Cloud-Umgebungen verstärkt, wo Angreifer latenzarmen Zugang zu Zielsystemen erreichen können, und in kryptografischen Anwendungen, wo Zeitvariationen direkt mit geheimen Werten korrelieren.

Lösung

Implementieren Sie Constant-Time-Algorithmen für alle sicherheitssensitiven Operationen, um sicherzustellen, dass die Ausführungszeit unabhängig von Eingabewerten oder geheimen Daten identisch bleibt. Verwenden Sie kryptografische Bibliotheken, die speziell Constant-Time-Implementierungen bereitstellen, wie solche, die bitweise Operationen anstelle von Verzweigungen verwenden. Ersetzen Sie Standard-String-Vergleichsfunktionen durch Constant-Time-Vergleichsfunktionen wie hmac.compare_digest() in Python oder crypto.timingSafeEqual() in Node.js. Für Passwortverifizierung berechnen und vergleichen Sie immer Hashes, auch wenn Benutzernamen nicht existieren. Fügen Sie kontrollierte zufällige Verzögerungen zu Operationen hinzu, wo Constant-Time-Implementierung unpraktisch ist, obwohl dies schwächeren Schutz bietet. Deaktivieren Sie hochauflösende Timing-APIs wo möglich und überwachen Sie auf timing-basierte Angriffe durch Anomalieerkennung. Prüfen Sie Code regelmäßig auf Timing-Schwachstellen mit statischen Analysetools, die für Seitenkanalerkennung entwickelt wurden.

Häufige Auswirkungen

AuswirkungDetails
Vertraulichkeit, ZugriffskontrolleBereich: Vertraulichkeit, Zugriffskontrolle

Angreifer können Zeitunterschiede messen, um sensible Daten zu extrahieren und Schutzmechanismen zu umgehen. Kryptografische Schlüssel, Passwörter und andere Geheimnisse können durch statistische Timing-Analyse wiederhergestellt werden, was unbefugten Zugriff ermöglicht.
VertraulichkeitBereich: Vertraulichkeit

Beobachtbare Timing-Diskrepanzen in kryptografischen Operationen geben direkt Informationen über geheime Schlüssel preis. Durch sorgfältige Messung und statistische Analyse können Angreifer komplette private Schlüssel wiederherstellen und die Vertraulichkeitsgarantien von Verschlüsselungssystemen brechen.

Beispielcode

Anfälliger Code (JavaScript/Node.js)

Der folgende Code demonstriert ein anfälliges Authentifizierungssystem mit mehreren Timing-Seitenkanalen:

const crypto = require('crypto');

class VulnerableAuth {
    constructor() {
        this.users = new Map();
    }

    // Anfällig: Vorzeitige Beendigung enthüllt gültige Benutzernamen
    async authenticate(username, password) {
        const user = this.users.get(username);

        // Timing-Leck #1: Schnelle Rückgabe für nicht existierende Benutzer
        if (!user) {
            return { success: false, error: 'Authentifizierung fehlgeschlagen' };
        }

        // Passwort-Hash nur für existierenden Benutzer berechnen
        // Timing-Leck #2: Hash-Berechnung nur für gültige Benutzer
        const hash = crypto.createHash('sha256')
            .update(password + user.salt)
            .digest('hex');

        // Timing-Leck #3: Standard-String-Vergleich
        // Beendet vorzeitig bei erster Nichtübereinstimmung
        if (hash === user.passwordHash) {
            return { success: true, token: this.generateToken(user) };
        }

        return { success: false, error: 'Authentifizierung fehlgeschlagen' };
    }

    // Anfällige HMAC-Verifizierung
    verifySignature(message, signature, secret) {
        const expected = crypto.createHmac('sha256', secret)
            .update(message)
            .digest('hex');

        // Timing-Leck: Zeichen-für-Zeichen-Vergleich
        // Angreifer kann korrekte Zeichen durch Timing bestimmen
        if (signature === expected) {
            return true;
        }
        return false;
    }
}

Der Code hat drei Timing-Schwachstellen: (1) sofortige Rückgabe für nicht existierende Benutzer, (2) Berechnung von Passwort-Hashes nur für gültige Benutzer und (3) Verwendung von Standard-String-Vergleich, der vorzeitig beendet.

Korrigierter Code (JavaScript/Node.js)

const crypto = require('crypto');

class SecureAuth {
    constructor() {
        this.users = new Map();
        // Vorberechnete Dummy-Werte für Timing-Konsistenz
        this.dummySalt = crypto.randomBytes(16).toString('hex');
        this.dummyHash = crypto.randomBytes(32).toString('hex');
    }

    async authenticate(username, password) {
        const user = this.users.get(username);

        // Korrigiert: Immer Werte abrufen, mit Dummy für nicht existierende Benutzer
        const salt = user ? user.salt : this.dummySalt;
        const storedHash = user ? user.passwordHash : this.dummyHash;

        // Korrigiert: Immer Hash berechnen unabhängig von Benutzerexistenz
        const computedHash = crypto.createHash('sha256')
            .update(password + salt)
            .digest('hex');

        // Korrigiert: Constant-Time-Vergleich verwenden
        const hashValid = crypto.timingSafeEqual(
            Buffer.from(computedHash, 'hex'),
            Buffer.from(storedHash, 'hex')
        );

        // Nur erfolgreich wenn Benutzer existiert UND Passwort korrekt ist
        const userExists = user !== undefined;
        const authenticated = hashValid && userExists;

        if (authenticated) {
            return { success: true, token: this.generateToken(user) };
        }

        // Intern loggen aber generischen Fehler zurückgeben
        this.logFailedAttempt(username, userExists);
        return { success: false, error: 'Authentifizierung fehlgeschlagen' };
    }

    verifySignature(message, signature, secret) {
        const expected = crypto.createHmac('sha256', secret)
            .update(message)
            .digest('hex');

        // Korrigiert: Constant-Time-Vergleich verhindert Timing-Angriffe
        try {
            return crypto.timingSafeEqual(
                Buffer.from(signature, 'hex'),
                Buffer.from(expected, 'hex')
            );
        } catch (e) {
            // Längenunterschied konsistent behandeln
            // Immer noch false zurückgeben aber in konstanter Zeit
            return false;
        }
    }

    // Zusätzlicher Schutz: Rate-Limiting
    checkRateLimit(username) {
        // Exponentielles Backoff für wiederholte Fehlschläge implementieren
        // Hilft statistische Timing-Angriffe zu mildern
    }

    logFailedAttempt(username, userExists) {
        // Internes Logging für Sicherheitsüberwachung
        // Diese Informationen niemals an Clients offenlegen
    }
}

Die Korrektur verwendet crypto.timingSafeEqual() für alle Vergleiche, führt Passwort-Hashing unabhängig von Benutzerexistenz mit Dummy-Werten durch und stellt sicher, dass alle Code-Pfade in konstanter Zeit ausgeführt werden.


Ausgenutzt in der Praxis

Apache Shiro HMAC-Timing-Angriff (Apache Shiro, 2019)

CVE-2019-10071 betraf Apache Shiro, ein beliebtes Java-Sicherheitsframework. Die Schwachstelle bestand, weil Shiro HMAC-Signaturen mit Javas Standard String.equals()-Methode verglich, die den Vergleich bei Finden des ersten nicht übereinstimmenden Zeichens beendet. Angreifer könnten diese Timing-Diskrepanz ausnutzen, um gültige Authentifizierungstokens zu fälschen, indem sie die korrekte Signatur Zeichen für Zeichen bestimmten und den kryptografischen Schutz vollständig umgingen.

OpenSSL CBC Padding Oracle (OpenSSL, 2003)

CVE-2003-0078 demonstrierte einen Timing-Angriff gegen OpenSSLs SSL-Implementierung. Die Schwachstelle trat auf, weil der Server die MAC-Berechnung für Nachrichten mit ungültigem Padding übersprang, was messbare Zeitunterschiede verursachte. Angreifer könnten dies ausnutzen, um SSL/TLS-Verkehr zu entschlüsseln, indem sie Padding-Fehler von MAC-Fehlern durch Timing-Analyse unterschieden, was zum Vaudenay Padding Oracle-Angriff führte, der sichere Kommunikationen weltweit betraf.

Cisco Router Passwort-Timing-Angriff (Cisco, 2014)

CVE-2014-0984 betraf Cisco-Router, bei denen die Passwortvalidierung vorzeitig beendet wurde, wenn ein falsches Zeichen gefunden wurde. Angreifer könnten das korrekte Passwort Zeichen für Zeichen bestimmen, indem sie Antwortzeiten maßen – korrekte Zeichen führten zu geringfügig längerer Verarbeitung, da die Validierung zum nächsten Zeichen fortfuhr. Dies reduzierte das Passwort-Knacken von potenziell Jahren auf Minuten für administrativen Zugriff.


Tools zum Testen/Ausnutzen

  • time-based-username-enumeration — Ruby-Tool zur Ausnutzung timing-basierter Benutzernamen-Enumerationsschwachstellen in Webanwendungen.

  • dudect — Tool zur Erkennung von Timing-Lecks in kryptografischem Code durch statistische Analyse von Ausführungszeiten.

  • ctgrind — Valgrind-basiertes Tool zur Überprüfung von Constant-Time-Implementierungen in C/C++-Code durch Erkennung von Verzweigungen auf geheimen Daten.


CVE-Beispiele

  • CVE-2019-10071 — Apache Shiro verglich HMAC-Signaturen mit Nicht-Constant-Time String.equals(), was Signaturfälschung ermöglichte.

  • CVE-2014-0984 — Cisco-Router-Passwortvalidierung beendete vorzeitig bei falschen Zeichen, was timing-basierte Passwortwiederherstellung ermöglichte.

  • CVE-2003-0078 — OpenSSL übersprang MAC-Berechnung für ungültiges Padding und schuf ein Timing-Orakel zur Entschlüsselung von SSL-Verkehr.

  • CVE-2003-0637 — Anwendung verwendete kürzeren Timeout für nicht existierende Benutzer, was Benutzernamen-Enumeration ermöglichte.

  • CVE-2005-0918 — Browser ermöglichte JavaScript, Dateiexistenz durch Ladezeit-Unterschiede zu erkennen.


Referenzen

  1. MITRE Corporation. "CWE-208: Observable Timing Discrepancy." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/208.html

  2. Kocher, Paul C. "Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems." Advances in Cryptology—CRYPTO '96. https://www.paulkocher.com/doc/TimingAttacks.pdf

  3. Brumley, David and Boneh, Dan. "Remote Timing Attacks are Practical." Computer Networks 48.5 (2005): 701-716.

  4. Bernstein, Daniel J. "Cache-timing attacks on AES." 2005. https://cr.yp.to/antiforgery/cachetiming-20050414.pdf