Offenlegung von Datenelementen an falsche Sitzung

Beschreibung

Offenlegung von Datenelementen an falsche Sitzung ist eine Schwachstelle, bei der das Produkt die Grenzen zwischen verschiedenen Benutzersitzungen nicht ordnungsgemäß durchsetzt, wodurch Daten der falschen Sitzung bereitgestellt oder von ihr verwendet werden können. Daten können von einer Sitzung in eine andere "durchsickern" durch Membervariablen von Singleton-Objekten, gemeinsam genutzte Pool-Objekte oder unsachgemäß eingeschränkte Variablen. Dies ist besonders häufig in Servlet-Umgebungen, wo Entwickler missverstehen, dass Servlets Singletons sind, die mehrere gleichzeitige Anfragen über verschiedene Threads verarbeiten, wodurch Benutzerdaten, die in Servlet-Memberfeldern gespeichert sind, über Sitzungen hinweg zugänglich werden.

Risiko

Sitzungsdaten-Offenlegung erzeugt schwerwiegende Vertraulichkeits- und Integritätsverletzungen. Benutzer können persönliche Informationen, Finanzdaten oder Authentifizierungsanmeldedaten anderer Benutzer sehen. In E-Commerce-Anwendungen könnten Benutzer die Warenkörbe oder Zahlungsinformationen anderer Kunden sehen. In Gesundheitssystemen könnten Patientendaten unbefugten Benutzern offengelegt werden. Die Schwachstelle ist besonders heimtückisch, weil sie sich nur unter bestimmten Timing-Bedingungen bei gleichzeitigen Anfragen manifestiert, was sie während des Testens schwer zu reproduzieren macht. Angreifer können absichtlich hohe Gleichzeitigkeit verursachen, um die Race Condition auszulösen und offengelegte Daten zu ernten.

Lösung

Speichern Sie niemals anfrage- oder sitzungsspezifische Daten in Memberfeldern von gemeinsam genutzten Objekten wie Servlets. Verwenden Sie lokale Variablen innerhalb von Methoden für anfragebezogene Daten. Für sitzungsbezogene Daten verwenden Sie explizit das HttpSession-Objekt. Implementieren Sie ordnungsgemäße Thread-Isolation unter Verwendung von ThreadLocal-Variablen, wenn thread-spezifischer Speicher benötigt wird. Verwenden Sie unveränderliche Objekte wo möglich. Führen Sie Code-Reviews durch, die speziell nach gemeinsam genutztem veränderlichem Zustand in nebenläufigen Kontexten suchen. Verwenden Sie statische Analysetools, die potenzielle Daten-Races und Sitzungsverwechslungs-Schwachstellen erkennen können. Erwägen Sie die Verwendung von anfragebezogenen Beans in Dependency-Injection-Frameworks.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Vertraulichkeit

Anwendungsdaten lesen - Sensible Benutzerdaten können von einer Sitzung in eine andere durchsickern und persönliche Informationen, Anmeldedaten oder andere vertrauliche Daten unbefugten Benutzern offenlegen.
IntegritätUmfang: Integrität

Anwendungsdaten modifizieren - Benutzer können versehentlich Daten anderer Sitzungen modifizieren, was Datenkorruption und unerwartetes Verhalten verursacht.

Beispielcode

Anfälliger Code

// Anfällig: Servlet mit Membervariable die Benutzerdaten speichert
import javax.servlet.http.*;
import java.io.*;

public class VulnerableServlet extends HttpServlet {
    // Anfällig: Membervariable über ALLE Anfragen/Sitzungen geteilt
    private String userName;
    private String userEmail;
    private ShoppingCart cart;
    private User currentUser;

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws IOException {

        // Thread 1: Benutzer "Alice" macht Anfrage
        userName = request.getParameter("name");  // userName = "Alice"

        // Kontextwechsel zu Thread 2: Benutzer "Bob" macht Anfrage
        // userName = "Bob" (überschreibt Alices Name)

        // Thread 1 setzt fort: Alice sieht Bobs Name!
        response.getWriter().println("Hallo, " + userName);

        // Anfällig: Benutzerdaten sickern zwischen Sitzungen
    }

    @Override
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws IOException {

        // Anfällig: Warenkorb als Membervariable gespeichert
        cart = new ShoppingCart();
        cart.addItem(request.getParameter("item"));

        // Anfrage eines anderen Benutzers könnte 'cart' überschreiben
        // bevor diese Anfrage abgeschlossen ist
        processCheckout(cart);  // Kann falschen Warenkorb verarbeiten!
    }
}

// Anfällig: Singleton-Service mit veränderlichem Zustand
public class VulnerableUserService {
    private static VulnerableUserService instance;

    // Anfällig: Aktueller Benutzer im Singleton gespeichert
    private User currentUser;
    private String authToken;

    public static VulnerableUserService getInstance() {
        if (instance == null) {
            instance = new VulnerableUserService();
        }
        return instance;
    }

    public void setCurrentUser(User user) {
        // Anfällig: Ein anderer Thread kann dies überschreiben
        this.currentUser = user;
    }

    public User getCurrentUser() {
        // Anfällig: Kann falschen Benutzer zurückgeben
        return this.currentUser;
    }

    public void performUserAction() {
        // Anfällig: currentUser kann sich geändert haben
        // zwischen setCurrentUser und diesem Aufruf
        auditLog.log(currentUser.getName() + " führte Aktion aus");
        // Kann falschen Benutzer protokollieren!
    }
}
// Anfällig: Verbindungspool gibt falsche Benutzerverbindung zurück
public class VulnerableConnectionPool {
    private Connection cachedConnection;
    private String lastUserId;

    public Connection getConnection(String userId) {
        // Anfällig: Zwischengespeicherte Verbindung mit Benutzerkontext
        if (cachedConnection == null || !lastUserId.equals(userId)) {
            cachedConnection = createConnection(userId);
            lastUserId = userId;
        }

        // Anfällig: Ein anderer Thread kann cachedConnection geändert haben
        // zwischen Prüfung und Rückgabe
        return cachedConnection;
    }
}

// Anfällig: Statischer Cache lässt Daten zwischen Benutzern durchsickern
public class VulnerableReportGenerator {
    // Anfällig: Statischer Cache für alle Sitzungen zugänglich
    private static Map<String, Report> reportCache = new HashMap<>();
    private static String lastReportUser;

    public Report generateReport(String userId) {
        Report report = createReport(userId);

        // Anfällig: Caching ohne ordnungsgemäße Isolation
        reportCache.put("lastReport", report);
        lastReportUser = userId;

        return report;
    }

    public Report getLastReport() {
        // Anfällig: Jeder Benutzer kann auf zuletzt generierten Bericht zugreifen
        return reportCache.get("lastReport");
    }
}
# Anfällig: Flask-Anwendung mit Zustand auf Modulebene
from flask import Flask, request

app = Flask(__name__)

# Anfällig: Variablen auf Modulebene zwischen Anfragen geteilt
current_user = None
user_cart = {}

@app.route('/login', methods=['POST'])
def login():
    global current_user
    # Anfällig: current_user zwischen ALLEN Anfragen geteilt
    current_user = request.form['username']
    return f"Angemeldet als {current_user}"

@app.route('/profile')
def profile():
    # Anfällig: Kann falschen Benutzer wegen Race Condition zurückgeben
    return f"Profil für {current_user}"

@app.route('/add_to_cart', methods=['POST'])
def add_to_cart():
    global user_cart
    item = request.form['item']
    # Anfällig: user_cart zwischen Sitzungen geteilt
    user_cart[item] = user_cart.get(item, 0) + 1
    return f"{item} hinzugefügt"

Korrigierter Code

// Korrigiert: Servlet verwendet lokale Variablen und Session
import javax.servlet.http.*;
import java.io.*;

public class SecureServlet extends HttpServlet {
    // Korrigiert: Keine Membervariablen für Benutzerdaten

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws IOException {

        // Korrigiert: Lokale Variable verwenden - thread-sicher
        String userName = request.getParameter("name");

        // Korrigiert: Benutzerdaten in Session speichern
        HttpSession session = request.getSession();
        session.setAttribute("userName", userName);

        response.getWriter().println("Hallo, " + userName);
    }

    @Override
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws IOException {

        // Korrigiert: Warenkorb des Benutzers aus Session holen
        HttpSession session = request.getSession();
        ShoppingCart cart = (ShoppingCart) session.getAttribute("cart");

        if (cart == null) {
            cart = new ShoppingCart();
            session.setAttribute("cart", cart);
        }

        // Korrigiert: Auf Session synchronisieren für Thread-Sicherheit
        synchronized (session) {
            cart.addItem(request.getParameter("item"));
        }

        processCheckout(cart);
    }
}

// Korrigiert: Thread-sicherer Service mit ThreadLocal
public class SecureUserService {
    private static SecureUserService instance;

    // Korrigiert: ThreadLocal für pro-Thread Benutzerkontext verwenden
    private ThreadLocal<User> currentUser = new ThreadLocal<>();
    private ThreadLocal<String> authToken = new ThreadLocal<>();

    public static synchronized SecureUserService getInstance() {
        if (instance == null) {
            instance = new SecureUserService();
        }
        return instance;
    }

    public void setCurrentUser(User user) {
        // Korrigiert: ThreadLocal bietet pro-Thread Speicherung
        currentUser.set(user);
    }

    public User getCurrentUser() {
        // Korrigiert: Gibt Benutzer dieses Threads zurück
        return currentUser.get();
    }

    public void performUserAction() {
        User user = currentUser.get();
        if (user == null) {
            throw new IllegalStateException("Kein Benutzer im Kontext");
        }
        auditLog.log(user.getName() + " führte Aktion aus");
    }

    // Korrigiert: ThreadLocal bereinigen um Speicherlecks zu verhindern
    public void clearContext() {
        currentUser.remove();
        authToken.remove();
    }
}

// Korrigiert: Ordnungsgemäße Anfragekontextverwaltung
public class SecureRequestContext {
    // Korrigiert: Anfragebezogener Kontext mit ThreadLocal
    private static final ThreadLocal<RequestContext> context =
        new ThreadLocal<>();

    public static void initContext(HttpServletRequest request) {
        RequestContext ctx = new RequestContext();
        ctx.userId = (String) request.getSession().getAttribute("userId");
        ctx.sessionId = request.getSession().getId();
        ctx.requestTime = System.currentTimeMillis();
        context.set(ctx);
    }

    public static RequestContext getContext() {
        return context.get();
    }

    public static void clearContext() {
        context.remove();
    }

    private static class RequestContext {
        String userId;
        String sessionId;
        long requestTime;
    }
}

// Korrigiert: Servlet-Filter für Kontextverwaltung
public class ContextFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException {
        try {
            // Korrigiert: Kontext zu Beginn der Anfrage initialisieren
            SecureRequestContext.initContext((HttpServletRequest) request);
            SecureUserService.getInstance().setCurrentUser(
                getUserFromSession((HttpServletRequest) request)
            );

            chain.doFilter(request, response);
        } finally {
            // Korrigiert: Kontext immer bereinigen
            SecureRequestContext.clearContext();
            SecureUserService.getInstance().clearContext();
        }
    }
}
// Korrigiert: Sitzungsbezogene Beans in Spring
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.stereotype.Component;

@Component
@SessionScope  // Korrigiert: Eine Instanz pro Sitzung
public class UserShoppingCart {
    private List<CartItem> items = new ArrayList<>();

    public void addItem(CartItem item) {
        items.add(item);
    }

    public List<CartItem> getItems() {
        return Collections.unmodifiableList(items);
    }
}

// Korrigiert: Anfragebezogener Service
@Component
@RequestScope  // Korrigiert: Eine Instanz pro Anfrage
public class RequestScopedService {
    private User currentUser;

    public void setCurrentUser(User user) {
        this.currentUser = user;
    }

    public User getCurrentUser() {
        return currentUser;
    }
}
# Korrigiert: Flask-Anwendung mit ordnungsgemäßer Session-Behandlung
from flask import Flask, request, session, g
from functools import wraps

app = Flask(__name__)
app.secret_key = 'sicherer-geheimer-schlüssel'

# Korrigiert: Anfragebezogener Speicher mit Flasks g-Objekt
@app.before_request
def before_request():
    # Korrigiert: g ist anfrage-lokal, sicher für gleichzeitige Anfragen
    g.user = session.get('user')
    g.cart = session.get('cart', {})

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    # Korrigiert: In Session speichern, nicht globaler Variable
    session['user'] = username
    return f"Angemeldet als {username}"

@app.route('/profile')
def profile():
    # Korrigiert: Zugriff vom anfrage-lokalen g-Objekt
    if g.user is None:
        return "Nicht angemeldet", 401
    return f"Profil für {g.user}"

@app.route('/add_to_cart', methods=['POST'])
def add_to_cart():
    item = request.form['item']
    # Korrigiert: Warenkorb in Session gespeichert, pro Benutzer isoliert
    cart = session.get('cart', {})
    cart[item] = cart.get(item, 0) + 1
    session['cart'] = cart
    return f"{item} hinzugefügt"

@app.teardown_request
def teardown_request(exception=None):
    # Korrigiert: Warenkorb zurück in Session speichern
    if hasattr(g, 'cart'):
        session['cart'] = g.cart

CVE-Beispiele

Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE aufgelistet. Das Schwachstellenmuster ist jedoch häufig in:

  • Java-Servlet-Anwendungen mit unsachgemäßer Zustandsverwaltung
  • Multi-Thread-Webanwendungen mit gemeinsam genutztem veränderlichem Zustand
  • Caching-Systemen, die Benutzerdaten nicht ordnungsgemäß isolieren

Referenzen

  1. MITRE Corporation. "CWE-488: Exposure of Data Element to Wrong Session." https://cwe.mitre.org/data/definitions/488.html
  2. Oracle. "The Java Servlet Specification - Threading Issues."
  3. OWASP. "Session Management Cheat Sheet."