Unsachgemäße Erhaltung der Konsistenz zwischen unabhängigen Darstellungen gemeinsamen Zustands

Beschreibung

Unsachgemäße Erhaltung der Konsistenz zwischen unabhängigen Darstellungen gemeinsamen Zustands tritt auf, wenn ein Produkt mehrere verteilte Komponenten unterhält, die jeweils lokale Kopien gemeinsamer Daten halten, aber nicht sicherstellt, dass alle Kopien systemweit synchronisiert bleiben. In verteilten Umgebungen und Systemen mit unabhängigen physischen Komponenten speichert jede typischerweise ihre eigene Kopie kritischer Daten wie Zustand oder Cache. Alle Komponenten müssen die gleiche "Sicht" beibehalten, um koordiniert zu arbeiten. Beispiele umfassen Social-Media-Plattformen, bei denen Benutzer auf verschiedenen Hosts identische Daten benötigen, oder Prozessoren mit Shadow Memory Management Units (MMUs), die übereinstimmende zugängliche Speicherbereiche haben müssen.

Risiko

Inkonsistente Zustandspflege schafft schwerwiegende Sicherheitsauswirkungen. Komponenten können unter falschen Annahmen arbeiten. Zugriffskontrollentscheidungen können inkonsistent werden. Sicherheitstoken können falsch dupliziert oder ungültig gemacht werden. Cache-Kohärenzprobleme können zu Privilege Escalation führen. Transaktionen können in falscher Reihenfolge ausgeführt werden. Benutzer können auf veraltete oder falsche Daten zugreifen. Angreifer können Race Conditions während Synchronisierungsfenstern ausnutzen. Die Systemintegrität kann kompromittiert werden.

Lösung

Minimieren Sie Nicht-Synchronisierungszeiträume und machen Sie den Aktualisierungsprozess so robust wie möglich. Implementieren Sie starke Konsistenzprotokolle für verteilte Komponenten. Verwenden Sie transaktionale Aktualisierungen mit atomaren Operationen. Implementieren Sie ordnungsgemäße Cache-Kohärenzprotokolle. Priorisieren Sie sicherheitskritische Aktualisierungen gegenüber allgemeinem Datenverkehr. Fügen Sie Überprüfung nach Zustandsänderungspropagierung hinzu. Implementieren Sie Konflikterkennungs- und Lösungsmechanismen. Verwenden Sie bei Bedarf verteilte Konsensalgorithmen.

Häufige Auswirkungen

AuswirkungDetails
SonstigesUmfang: Sonstiges

Unerwarteter Zustand - Eine oder mehrere Komponenten/Subsysteme könnten unter falschen Annahmen über den tatsächlichen Systemzustand arbeiten.

Beispielcode und Lösung

Verwundbarer Code

# VERWUNDBAR: Verteilter Cache ohne Synchronisation

import threading
import time

class VulnerableCacheNode:
    def __init__(self, node_id):
        self.node_id = node_id
        self.local_cache = {}
        self.version = 0

    def get(self, key):
        # VERWUNDBAR: Gibt lokale Kopie zurück, ohne Aktualität zu prüfen
        return self.local_cache.get(key)

    def set(self, key, value):
        # VERWUNDBAR: Aktualisiert nur lokale Kopie
        # Keine Synchronisation mit anderen Knoten
        self.local_cache[key] = value
        self.version += 1

    def receive_update(self, key, value, version):
        # VERWUNDBAR: Keine Reihenfolgegarantie
        # Race Condition: Aktualisierungen können in falscher Reihenfolge ankommen
        self.local_cache[key] = value


# VERWUNDBAR: Benutzerberechtigungen ohne Konsistenz
class VulnerablePermissionSystem:
    def __init__(self):
        self.nodes = []

    def revoke_permission(self, user_id, permission):
        # VERWUNDBAR: Sendet Widerruf, wartet aber nicht auf Bestätigung
        for node in self.nodes:
            # Fire-and-Forget - keine Garantie, dass alle Knoten aktualisiert wurden
            threading.Thread(target=node.remove_permission,
                           args=(user_id, permission)).start()

        # VERWUNDBAR: Kehrt sofort zurück, bevor Propagierung abgeschlossen
        # Benutzer hat möglicherweise noch Berechtigung auf einigen Knoten

    def check_permission(self, node, user_id, permission):
        # VERWUNDBAR: Jeder Knoten hat möglicherweise veraltete Berechtigungsdaten
        return node.has_permission(user_id, permission)


# Angriff: Synchronisierungsverzögerung ausnutzen
def exploit_sync_delay():
    system = VulnerablePermissionSystem()

    # Admin widerruft Berechtigung des Angreifers
    system.revoke_permission("attacker", "admin_access")

    # Angreifer versucht sofort alle Knoten
    # Einige Knoten haben den Widerruf noch nicht erhalten
    for node in system.nodes:
        if system.check_permission(node, "attacker", "admin_access"):
            # ANGRIFF: Knoten mit veralteten Berechtigungen gefunden
            perform_admin_action(node)
            break
// VERWUNDBAR: Shadow-MMU ohne ordnungsgemäße Synchronisation

module vulnerable_shadow_mmu (
    input wire clk,
    input wire reset_n,
    input wire [31:0] update_addr,
    input wire [31:0] update_data,
    input wire update_valid,
    input wire [31:0] check_addr,
    output reg access_allowed
);

    // Lokale Kopie der Zugriffsberechtigungen
    reg [31:0] permission_table [0:255];

    // VERWUNDBAR: Keine Synchronisation mit Master-MMU
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            integer i;
            for (i = 0; i < 256; i = i + 1) begin
                permission_table[i] <= 32'h0;
            end
        end
        else if (update_valid) begin
            // VERWUNDBAR: Aktualisierungen kommen ohne Reihenfolge
            // Race Condition mit Zugriffsüberprüfungen
            permission_table[update_addr[7:0]] <= update_data;
        end
    end

    // VERWUNDBAR: Prüfung gegen möglicherweise veraltete Daten
    always @(*) begin
        access_allowed = permission_table[check_addr[7:0]][0];
    end

    // Kein Mechanismus, um:
    // - Zu überprüfen, ob Aktualisierung aus legitimer Quelle kam
    // - Sicherzustellen, dass alle Shadow-MMUs gleiche Aktualisierung erhalten haben
    // - Inkonsistenz zwischen Master und Shadow zu erkennen

endmodule
// VERWUNDBAR: Verteilter Session-Speicher ohne Konsistenz

public class VulnerableSessionStore {
    private Map<String, Session> localSessions = new ConcurrentHashMap<>();
    private List<VulnerableSessionStore> peers;

    public void invalidateSession(String sessionId) {
        // Lokal entfernen
        localSessions.remove(sessionId);

        // VERWUNDBAR: Asynchroner Broadcast an Peers ohne Bestätigung
        for (VulnerableSessionStore peer : peers) {
            CompletableFuture.runAsync(() -> {
                peer.receiveInvalidation(sessionId);
            });
        }
        // VERWUNDBAR: Kehrt zurück, bevor alle Peers Invalidierung bestätigt haben
    }

    public boolean isSessionValid(String sessionId) {
        // VERWUNDBAR: Prüft nur lokale Kopie
        // Kann true für invalidierte Session zurückgeben, wenn Aktualisierung noch nicht angekommen
        return localSessions.containsKey(sessionId);
    }

    public void receiveInvalidation(String sessionId) {
        // VERWUNDBAR: Keine Reihenfolgegarantie
        // Netzwerkverzögerung könnte veraltete Session gültig lassen
        localSessions.remove(sessionId);
    }
}

Sichere Lösung

# SICHER: Verteilter Cache mit stärker Konsistenz

import threading
import time
from collections import defaultdict

class SecureCacheNode:
    def __init__(self, node_id, coordinator):
        self.node_id = node_id
        self.local_cache = {}
        self.versions = {}
        self.coordinator = coordinator
        self.lock = threading.Lock()

    def get(self, key):
        with self.lock:
            # SICHER: Version mit Koordinator prüfen, bevor zurückgegeben wird
            local_version = self.versions.get(key, 0)
            current_version = self.coordinator.get_current_version(key)

            if local_version < current_version:
                # Veraltete Daten - frische Kopie holen
                self.local_cache[key], self.versions[key] = \
                    self.coordinator.get_authoritative(key)

            return self.local_cache.get(key)

    def set(self, key, value):
        # SICHER: Aktualisierung über zentrale Autorität koordinieren
        success = self.coordinator.request_update(key, value, self.node_id)
        if success:
            with self.lock:
                self.local_cache[key] = value
                self.versions[key] = self.coordinator.get_current_version(key)
        return success


# SICHER: Berechtigungssystem mit stärker Konsistenz
class SecurePermissionSystem:
    def __init__(self):
        self.nodes = []
        self.pending_revocations = {}
        self.lock = threading.Lock()

    def revoke_permission(self, user_id, permission):
        revocation_id = generate_unique_id()

        with self.lock:
            # Ausstehenden Widerruf verfolgen
            self.pending_revocations[revocation_id] = {
                'user_id': user_id,
                'permission': permission,
                'confirmed_nodes': set(),
                'total_nodes': len(self.nodes)
            }

        # SICHER: Auf Bestätigung aller Knoten warten
        confirmation_events = []
        for node in self.nodes:
            event = threading.Event()
            confirmation_events.append(event)
            node.remove_permission_with_ack(
                user_id, permission, revocation_id, event)

        # SICHER: Blockieren, bis alle Knoten bestätigt haben
        all_confirmed = all(event.wait(timeout=30) for event in confirmation_events)

        if not all_confirmed:
            # Teilweise Propagierung behandeln - ausfallsicher
            self.emergency_revoke(user_id, permission)
            raise SynchronizationError("Widerruf könnte nicht an alle Knoten propagiert werden")

        with self.lock:
            del self.pending_revocations[revocation_id]

    def check_permission(self, node, user_id, permission):
        with self.lock:
            # SICHER: Erst auf ausstehende Widerrufe prüfen
            for revocation in self.pending_revocations.values():
                if (revocation['user_id'] == user_id and
                    revocation['permission'] == permission):
                    # Widerruf in Bearbeitung - Zugriff verweigern
                    return False

        return node.has_permission(user_id, permission)

    def emergency_revoke(self, user_id, permission):
        # Allen Zugriff blockieren, während Synchronisation läuft
        for node in self.nodes:
            node.block_user(user_id)
// SICHER: Shadow-MMU mit Synchronisierungsüberprüfung

module secure_shadow_mmu (
    input wire clk,
    input wire reset_n,
    input wire [31:0] update_addr,
    input wire [31:0] update_data,
    input wire [31:0] update_sequence,  // Sequenznummer für Reihenfolge
    input wire update_valid,
    input wire [31:0] master_checksum,  // Prüfsumme von Master-MMU
    input wire [31:0] check_addr,
    output reg access_allowed,
    output reg sync_error
);

    reg [31:0] permission_table [0:255];
    reg [31:0] expected_sequence;
    reg [31:0] local_checksum;

    // SICHER: Synchronisierungszustand verfolgen
    reg synchronized;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            integer i;
            for (i = 0; i < 256; i = i + 1) begin
                permission_table[i] <= 32'h0;
            end
            expected_sequence <= 32'h0;
            local_checksum <= 32'h0;
            synchronized <= 1'b1;
            sync_error <= 1'b0;
        end
        else if (update_valid) begin
            // SICHER: Aktualisierungssequenz überprüfen
            if (update_sequence == expected_sequence) begin
                permission_table[update_addr[7:0]] <= update_data;
                expected_sequence <= expected_sequence + 1;

                // Prüfsumme aktualisieren
                local_checksum <= local_checksum ^ update_data;
            end
            else if (update_sequence > expected_sequence) begin
                // SICHER: Aktualisierung verpasst - als nicht synchronisiert markieren
                synchronized <= 1'b0;
                sync_error <= 1'b1;
            end
            // Doppelte/alte Aktualisierungen ignorieren
        end
    end

    // SICHER: Periodische Konsistenzprüfung
    always @(posedge clk) begin
        if (synchronized && (local_checksum != master_checksum)) begin
            // Prüfsummen-Diskrepanz - nicht synchronisiert
            synchronized <= 1'b0;
            sync_error <= 1'b1;
        end
    end

    // SICHER: Zugriff verweigern, wenn nicht synchronisiert (ausfallsicher)
    always @(*) begin
        if (!synchronized) begin
            access_allowed = 1'b0;  // Ausfallsicher bei Nicht-Synchronisation
        end
        else begin
            access_allowed = permission_table[check_addr[7:0]][0];
        end
    end

endmodule
// SICHER: Verteilter Session-Speicher mit stärker Konsistenz

public class SecureSessionStore {
    private Map<String, Session> localSessions = new ConcurrentHashMap<>();
    private Map<String, Long> sessionVersions = new ConcurrentHashMap<>();
    private List<SecureSessionStore> peers;
    private final Object syncLock = new Object();

    public void invalidateSession(String sessionId) throws SynchronizationException {
        long invalidationVersion = System.nanoTime();

        // SICHER: Zwei-Phasen-Commit für Session-Invalidierung
        List<CompletableFuture<Boolean>> preparePhase = new ArrayList<>();

        // Phase 1: Vorbereitung
        for (SecureSessionStore peer : peers) {
            preparePhase.add(CompletableFuture.supplyAsync(() ->
                peer.prepareInvalidation(sessionId, invalidationVersion)));
        }

        // Auf Bestätigung aller Peers warten
        boolean allPrepared = preparePhase.stream()
            .allMatch(future -> {
                try {
                    return future.get(10, TimeUnit.SECONDS);
                } catch (Exception e) {
                    return false;
                }
            });

        if (!allPrepared) {
            // Abbruch - Rollback
            for (SecureSessionStore peer : peers) {
                peer.abortInvalidation(sessionId);
            }
            throw new SynchronizationException("Vorbereitung aller Knoten fehlgeschlagen");
        }

        // Phase 2: Commit
        List<CompletableFuture<Void>> commitPhase = new ArrayList<>();
        for (SecureSessionStore peer : peers) {
            commitPhase.add(CompletableFuture.runAsync(() ->
                peer.commitInvalidation(sessionId, invalidationVersion)));
        }

        // Lokal entfernen nach allen Commits
        CompletableFuture.allOf(commitPhase.toArray(new CompletableFuture[0])).join();
        localSessions.remove(sessionId);
        sessionVersions.put(sessionId, invalidationVersion);
    }

    public boolean isSessionValid(String sessionId) {
        synchronized (syncLock) {
            // SICHER: Prüfen, ob Invalidierung in Bearbeitung
            if (pendingInvalidations.contains(sessionId)) {
                return false;
            }

            // SICHER: Lokale Version mit Quorum abgleichen
            Long localVersion = sessionVersions.get(sessionId);
            if (!verifyVersionWithQuorum(sessionId, localVersion)) {
                // Versionsdiskrepanz - von Peers aktualisieren
                refreshSession(sessionId);
            }

            return localSessions.containsKey(sessionId);
        }
    }

    private boolean verifyVersionWithQuorum(String sessionId, Long localVersion) {
        int matches = 0;
        int quorum = (peers.size() / 2) + 1;

        for (SecureSessionStore peer : peers) {
            if (Objects.equals(peer.getVersion(sessionId), localVersion)) {
                matches++;
                if (matches >= quorum) {
                    return true;
                }
            }
        }
        return false;
    }
}

CVE-Beispiele

Schwachstellen in der Zustandskonsistenz wurden in verschiedenen verteilten Systemen gefunden, darunter Cloud-Plattformen, CDN-Netzwerke und Mehrprozessorsysteme, bei denen Angreifer Synchronisierungsfenster ausnutzten, um Sicherheitskontrollen zu umgehen.


Verwandte CWEs

  • CWE-664: Unsachgemäße Kontrolle einer Ressource während ihrer Lebensdauer (übergeordnet)
  • CWE-1249: Admin-Tool auf Anwendungsebene mit inkonsistenter Sicht (untergeordnet)
  • CWE-1251: Gespiegelte Regionen mit unterschiedlichen Werten (untergeordnet)
  • CWE-362: Nebenläufige Ausführung mit gemeinsamer Ressource bei unsachgemäßer Synchronisation (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1250: Improper Preservation of Consistency Between Independent Representations of Shared State." https://cwe.mitre.org/data/definitions/1250.html
  2. Lamport, Leslie. "The Part-Time Parliament" (Paxos Consensus)
  3. Ongaro, Diego. "In Search of an Understandable Consensus Algorithm" (Raft)