Exponierung von Ressourcen an falsche Sphäre

Beschreibung

Exponierung von Ressourcen an falsche Sphäre tritt auf, wenn ein Produkt eine Ressource der falschen Kontrollsphäre aussetzt und unbeabsichtigten Akteuren unangemessenen Zugriff auf die Ressource gewährt. Eine "Kontrollsphäre" ist eine Menge von Ressourcen und Verhaltensweisen, die einem einzelnen Akteur oder einer Gruppe von Akteuren zugänglich sind. Produkte definieren typischerweise mehrere Sphären—Administratoren, reguläre Benutzer, Gäste, externe Systeme—jeweils mit unterschiedlichen Zugriffsebenen. Wenn Ressourcen, die für eine Sphäre bestimmt sind, einer anderen zugänglich sind, können Angreifer auf sensible Daten, Funktionalitäten oder Dienste zugreifen, zu denen sie keinen Zugang haben sollten, was zu Privilegieneskalation, Informationsoffenlegung oder unbefugten Operationen führt.

Risiko

Die Exponierung von Ressourcen an falsche Sphären ist ein grundlegender Zugriffskontrollfehler. CVE-2025-49574 im Quarkus-Framework ermöglicht das Durchsickern von Transaktionsdaten einschließlich Sicherheitsanmeldedaten über Transaktionsgrenzen hinweg aufgrund von Kontext-Duplikationsproblemen. CVE-2024-5313 bei Schneider Electric exponiert SSH-Schnittstellen über Produktnetzwerkschnittstellen. CVE-2025-6788 exponiert TGML-Diagramme an falsche Kontrollsphären. Diese Schwachstellen wurden in IBM Business Automation, HPE-Servern und Lenovo Intel DTT Software gefunden. Wenn Ressourcen Kontrollsphärengrenzen überschreiten, können Angreifer auf administrative Funktionen zugreifen, vertrauliche Daten lesen oder Systeme über ihre Autorisierungsebene hinaus manipulieren.

Lösung

Implementieren Sie ordnungsgemäße Zugriffskontrollgrenzen zwischen Kontrollsphären. Verwenden Sie das Prinzip der minimalen Rechte—Ressourcen sollten nur dem minimalen Satz von Akteuren zugänglich sein, die sie benötigen. Validieren Sie die Autorisierung an jedem Zugriffspunkt, nicht nur auf der UI-Ebene. Implementieren Sie Netzwerksegmentierung, um Ressourcen zwischen Sphären zu isolieren. Verwenden Sie separate Prozesse oder Container für unterschiedliche Vertrauensebenen. Prüfen Sie die Ressourcenexponierung während Design und Test. Implementieren Sie ordnungsgemäße Session-Isolierung, um sphärenübergreifende Datenlecks zu verhindern. Verwenden Sie defensive Programmierung, um den Kontext vor dem Zugriff auf sensible Ressourcen zu validieren.

Häufige Konsequenzen

AuswirkungDetails
VertraulichkeitBereich: Informationsoffenlegung

Ressourcen für privilegierte Benutzer könnten weniger privilegierten oder externen Akteuren zugänglich sein.
IntegritätBereich: Unbefugte Modifikation

Angreifer in der falschen Sphäre könnten Ressourcen modifizieren, auf die sie keinen Zugriff haben sollten.
ZugriffskontrolleBereich: Privilegieneskalation

Zugriff auf Ressourcen der falschen Sphäre ermöglicht oft weitere Privilegieneskalation.

Beispielcode + Lösungscode

Verwundbarer Code

// VERWUNDBAR: Admin-Funktionalität allen authentifizierten Benutzern exponiert
@RestController
@RequestMapping("/api")
public class AdminController {

    // Keine Rollenprüfung - jeder authentifizierte Benutzer kann zugreifen!
    @GetMapping("/admin/users")
    public List<User> getAllUsers() {
        return userRepository.findAll();  // Enthält sensible Daten
    }

    @DeleteMapping("/admin/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userRepository.deleteById(id);  // Jeder Benutzer kann löschen!
    }
}

// VERWUNDBAR: Thread-lokaler Kontext leckt über Anfragen hinweg
public class RequestContext {
    private static ThreadLocal<UserSession> currentSession = new ThreadLocal<>();

    public static void setSession(UserSession session) {
        currentSession.set(session);
    }

    public static UserSession getSession() {
        return currentSession.get();  // Könnte vorherige Benutzer-Session zurückgeben!
    }

    // Fehlende Bereinigung - Session leckt zur nächsten Anfrage
}
# VERWUNDBAR: Debug-Endpoint in Produktion exponiert
from flask import Flask, request, jsonify

app = Flask(__name__)

# Debug-Route ohne Authentifizierung zugänglich!
@app.route('/debug/config')
def debug_config():
    # Exponiert interne Konfiguration an jeden
    return jsonify({
        'database_url': app.config['DATABASE_URL'],
        'secret_key': app.config['SECRET_KEY'],
        'api_keys': app.config['EXTERNAL_API_KEYS']
    })

# VERWUNDBAR: Interne API extern exponiert
@app.route('/internal/metrics')
def internal_metrics():
    # Sollte nur vom internen Netzwerk zugänglich sein
    return jsonify(get_system_metrics())
// VERWUNDBAR: Mandantenübergreifende Datenexponierung
class MultiTenantService {
    constructor() {
        this.dataCache = new Map();  // Geteilter Cache über Mandanten!
    }

    async getData(userId, dataId) {
        const cacheKey = dataId;  // Keine Mandantenisolierung im Cache-Schlüssel

        if (this.dataCache.has(cacheKey)) {
            // Gibt möglicherweise Daten eines anderen Mandanten zurück!
            return this.dataCache.get(cacheKey);
        }

        const data = await this.db.findById(dataId);
        this.dataCache.set(cacheKey, data);
        return data;
    }
}

// VERWUNDBAR: Datei-Upload für falsche Sphäre zugänglich
app.post('/upload', upload.single('file'), (req, res) => {
    // Dateien in öffentlich zugänglichem Verzeichnis gespeichert
    const path = `/public/uploads/${req.file.filename}`;
    res.json({ url: path });
});

Lösungscode

// SICHER: Ordnungsgemäße rollenbasierte Zugriffskontrolle
@RestController
@RequestMapping("/api")
public class SecureAdminController {

    @GetMapping("/admin/users")
    @PreAuthorize("hasRole('ADMIN')")  // Nur Admins können zugreifen
    @Secured("ROLE_ADMIN")
    public List<UserDTO> getAllUsers() {
        // DTO ohne sensible Felder zurückgeben
        return userRepository.findAll().stream()
            .map(this::toSafeDTO)
            .collect(Collectors.toList());
    }

    @DeleteMapping("/admin/users/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    @Transactional
    public void deleteUser(@PathVariable Long id, Principal principal) {
        // Aktion protokollieren
        auditLog.info("Benutzer {} gelöscht von Admin {}",
            id, principal.getName());
        userRepository.deleteById(id);
    }

    private UserDTO toSafeDTO(User user) {
        // Sensible Felder ausschließen
        return new UserDTO(user.getId(), user.getUsername(), user.getEmail());
    }
}

// SICHER: Ordnungsgemäße Kontextbereinigung
public class SecureRequestContext implements AutoCloseable {
    private static final ThreadLocal<UserSession> currentSession = new ThreadLocal<>();

    public static void setSession(UserSession session) {
        currentSession.set(session);
    }

    public static UserSession getSession() {
        UserSession session = currentSession.get();
        if (session == null) {
            throw new SecurityException("Keine Session im Kontext");
        }
        return session;
    }

    @Override
    public void close() {
        // Immer bereinigen um Lecks zu verhindern
        currentSession.remove();
    }
}

// Verwendung mit try-with-resources
try (SecureRequestContext ctx = new SecureRequestContext()) {
    SecureRequestContext.setSession(userSession);
    processRequest();
}  // Automatisch bereinigt
# SICHER: Debug-Endpoints in Produktion deaktiviert
from flask import Flask, request, jsonify, abort
import os

app = Flask(__name__)

def require_debug_mode(f):
    """Nur im Debug-Modus erlauben."""
    @wraps(f)
    def decorated(*args, **kwargs):
        if not app.debug:
            abort(404)  # Endpoint in Produktion verstecken
        return f(*args, **kwargs)
    return decorated

@app.route('/debug/config')
@require_debug_mode  # Funktioniert nur im Debug-Modus
def debug_config():
    # Ausgabe auch im Debug-Modus bereinigen
    return jsonify({
        'environment': app.config['ENV'],
        'debug': app.debug
        # Niemals Geheimnisse exponieren
    })

# SICHER: Interne Endpoints durch Netzwerk und Authentifizierung geschützt
@app.route('/internal/metrics')
@require_internal_network  # Quell-IP prüfen
@require_service_auth     # Service-zu-Service-Auth erfordern
def internal_metrics():
    return jsonify(get_system_metrics())

def require_internal_network(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        client_ip = request.remote_addr
        if not is_internal_ip(client_ip):
            abort(403)
        return f(*args, **kwargs)
    return decorated
// SICHER: Mandantenisoliertes Caching
class SecureMultiTenantService {
    constructor() {
        this.dataCache = new Map();
    }

    async getData(tenantId, userId, dataId) {
        // Mandant im Cache-Schlüssel für Isolierung einbeziehen
        const cacheKey = `${tenantId}:${dataId}`;

        if (this.dataCache.has(cacheKey)) {
            const cached = this.dataCache.get(cacheKey);
            // Mandanteneigentum verifizieren
            if (cached.tenantId !== tenantId) {
                throw new SecurityError('Mandantenübergreifender Zugriff verweigert');
            }
            return cached;
        }

        const data = await this.db.findById(dataId);

        // Verifizieren, dass Daten zum anfragenden Mandanten gehören
        if (data.tenantId !== tenantId) {
            throw new SecurityError('Daten gehören nicht zum Mandanten');
        }

        this.dataCache.set(cacheKey, data);
        return data;
    }
}

// SICHER: Datei-Uploads in privatem Ort mit Zugriffskontrolle gespeichert
const UPLOAD_BASE = '/private/uploads';

app.post('/upload', authenticate, upload.single('file'), async (req, res) => {
    const userId = req.user.id;

    // In benutzerspezifischem privatem Verzeichnis speichern
    const userDir = path.join(UPLOAD_BASE, userId.toString());
    await fs.mkdir(userDir, { recursive: true });

    const filename = `${crypto.randomUUID()}-${req.file.originalname}`;
    const filepath = path.join(userDir, filename);

    await fs.rename(req.file.path, filepath);

    // Signierte URL für Zugriffskontrolle zurückgeben
    const signedUrl = generateSignedUrl(filepath, req.user);
    res.json({ url: signedUrl });
});

// Dateien durch zugriffskontrollierten Endpoint bereitgestellt
app.get('/files/:userId/:filename', authenticate, async (req, res) => {
    // Verifizieren, dass Benutzer auf diese Datei zugreifen kann
    if (req.user.id !== req.params.userId && !req.user.isAdmin) {
        return res.status(403).json({ error: 'Zugriff verweigert' });
    }

    const filepath = path.join(UPLOAD_BASE, req.params.userId, req.params.filename);
    res.sendFile(filepath);
});

Ausgenutzt in der Praxis

Quarkus Framework Kontextleck (Quarkus, 2025)

CVE-2025-49574 im Quarkus-Framework vor 3.24.1 ermöglicht das Durchsickern von Transaktionsdaten einschließlich Sicherheitsanmeldedaten und Metadaten über Transaktionsgrenzen hinweg, wenn Vert.x-Kontexte mehrfach dupliziert werden, was Isolierungsprinzipien verletzt.

Schneider Electric SSH-Exponierung (Schneider Electric, 2024)

CVE-2024-5313 in Schneider Electric-Produkten exponiert SSH-Schnittstellen an die Produktnetzwerkschnittstelle, was Port-Scanning, Fingerprinting und potentielle Denial-of-Service-Angriffe auf exponierte SSH-Dienste ermöglicht.

TGML-Diagramm-Exponierung (2025)

CVE-2025-6788 exponiert TGML-Diagramm-Ressourcen an falsche Kontrollsphären und ermöglicht authentifizierten Benutzern den Zugriff auf Diagramme, auf die sie keinen Zugriff haben sollten.


Werkzeuge zum Testen/Ausnutzen

  • Burp Suite — Zugriffskontrollgrenzen testen.

  • OWASP ZAP — Automatisierte Zugriffskontrolltests.

  • Postman — API-Zugriff über verschiedene Benutzerrollen testen.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-668: Exposure of Resource to Wrong Sphere." https://cwe.mitre.org/data/definitions/668.html

  2. OWASP. "Broken Access Control." https://owasp.org/Top10/A01_2021-Broken_Access_Control/