Klartext-Speicherung sensibler Informationen in einem Cookie

Beschreibung

Klartext-Speicherung sensibler Informationen in einem Cookie ist eine Schwachstelle, die auftritt, wenn ein Produkt sensible Informationen im Klartext in Browser-Cookies speichert. Cookies werden auf dem Computer des Clients gespeichert und mit jeder HTTP-Anfrage übertragen, was sie für Benutzer, Browser-Erweiterungen und potenziell für Angreifer durch XSS-Schwachstellen oder physischen Zugriff auf das Gerät zugänglich macht. Sensible Informationen, die in Klartext-Cookies gespeichert sind, können leicht gelesen und möglicherweise modifiziert werden. Selbst kodierte Daten (wie Base64) bieten keine echte Sicherheit, da Angreifer leicht die Kodierungsmethode bestimmen und die Informationen dekodieren können.

Risiko

Klartext-Cookie-Speicherung exponiert sensible Daten durch mehrere Angriffsvektoren. Cross-Site-Scripting (XSS)-Schwachstellen ermöglichen Angreifern, Cookies remote zu stehlen. Physischer Zugriff auf ein Gerät ermöglicht direktes Lesen von Cookie-Dateien. Browser-Erweiterungen können auf Cookie-Daten zugreifen. Netzwerk-Angreifer können über unverschlüsselte HTTP-Verbindungen übertragene Cookies abfangen. Man-in-the-Middle-Angriffe können Cookies erfassen, selbst wenn die initiale Verbindung HTTPS verwendet, falls Cookie-Sicherheitsflags nicht gesetzt sind. Das Risiko wird verstärkt, weil Cookies über Browser-Sitzungen hinweg bestehen und auch nach dem Logout der Benutzer zugänglich bleiben können, wenn sie nicht ordnungsgemäß ablaufen. Exponierte Anmeldedaten ermöglichen Kontoübernahme, während exponierte Session-Daten Session-Hijacking ermöglichen können.

Lösung

Speichern Sie niemals sensible Informationen direkt in Cookies. Verwenden Sie für Authentifizierung serverseitige Sessions mit nur einer sicheren, zufälligen Session-ID im Cookie. Wenn Cookies Daten enthalten müssen, verschlüsseln Sie sie serverseitig mit authentifizierter Verschlüsselung vor der Speicherung und entschlüsseln Sie nur auf dem Server. Setzen Sie das HttpOnly-Flag, um JavaScript-Zugriff zu verhindern und XSS-basierten Cookie-Diebstahl zu mildern. Setzen Sie das Secure-Flag, um sicherzustellen, dass Cookies nur über HTTPS übertragen werden. Verwenden Sie das SameSite-Attribut, um Cross-Site-Request-Forgery zu verhindern. Implementieren Sie kurze Cookie-Ablaufzeiten für sensible Daten. Speichern Sie sensible Benutzerdaten serverseitig und referenzieren Sie sie über Session-IDs, anstatt sie in Cookies aufzunehmen. Speichern Sie niemals Anmeldedaten, Finanzdaten oder persönliche Informationen in Cookies.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Angreifer können sensible Informationen, die in Cookies gespeichert sind, durch verschiedene Methoden lesen, einschließlich XSS-Angriffe, physischen Zugriff, Browser-Erweiterungen oder Netzwerkabfangen.
Authentifizierung, ZugriffskontrolleBereich: Authentifizierung, Zugriffskontrolle

Exponierte Anmeldedaten ermöglichen Kontoübernahme. Exponierte Session-Daten ermöglichen Session-Hijacking. Angreifer können Benutzer imitieren und auf geschützte Ressourcen zugreifen.

Beispielcode

Anfälliger Code (Java/PHP)

Die folgenden Beispiele demonstrieren Klartext-Cookie-Speicherungs-Schwachstellen:

// Anfällig: Account-ID in Klartext-Cookie speichern
import javax.servlet.http.*;

public class VulnerableCookieStorage extends HttpServlet {

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) {

        String accountId = request.getParameter("account");

        // Anfällig: Klartext-Account-ID im Cookie
        Cookie cookie = new Cookie("userAccountID", accountId);
        cookie.setMaxAge(3600);  // 1 Stunde
        response.addCookie(cookie);

        // Angreifer kann Account-ID lesen und modifizieren!
    }

    // Anfällig: Anmeldedaten in Cookie speichern
    public void persistLogin(HttpServletResponse response,
                            String username, String password) {

        // Anfällig: Klartext-Benutzername im Cookie
        Cookie userCookie = new Cookie("username", username);
        response.addCookie(userCookie);

        // Extrem anfällig: Klartext-Passwort im Cookie!
        Cookie passCookie = new Cookie("password", password);
        response.addCookie(passCookie);

        // Keine HttpOnly-, Secure- oder SameSite-Flags!
    }

    // Anfällig: Rolle/Berechtigungen in Cookie speichern
    public void setUserRole(HttpServletResponse response, String role) {
        // Anfällig: Benutzer kann modifizieren um Admin-Zugriff zu erlangen
        Cookie roleCookie = new Cookie("userRole", role);
        response.addCookie(roleCookie);

        // Angreifer ändert Cookie zu "admin" und erlangt Privilegien!
    }
}
<?php
// Anfällig: Login-Anmeldedaten in Klartext-Cookie speichern
function persistLogin($username, $password) {
    // Anfällig: Klartext-Anmeldedaten im Cookie
    $data = array("username" => $username, "password" => $password);
    setcookie("userdata", json_encode($data), time() + 3600);

    // Jeder mit Cookie-Zugriff erhält Anmeldedaten!
}

// Anfällig: Sensible Benutzerdaten in Cookie speichern
function setUserSession($userData) {
    // Anfällig: PII in Klartext-Cookie
    $sessionData = array(
        "user_id" => $userData['id'],
        "email" => $userData['email'],
        "ssn" => $userData['ssn'],  // Sozialversicherungsnummer im Cookie!
        "credit_card" => $userData['card']  // Kreditkarte im Cookie!
    );

    setcookie("session", base64_encode(json_encode($sessionData)));
    // Base64 ist KEINE Verschlüsselung - leicht dekodierbar!
}

// Anfällig: Authentifizierungstoken ohne Sicherheitsflags speichern
function setAuthToken($token) {
    // Anfällig: Fehlende Sicherheitsflags
    setcookie(
        "auth_token",
        $token,
        time() + 86400,
        "/"
        // Fehlend: secure, httponly, samesite Flags!
    );
}
?>
# Anfällig: Flask Klartext-Cookie-Speicherung
from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    if authenticate(username, password):
        response = make_response(redirect('/dashboard'))

        # Anfällig: Klartext-Anmeldedaten in Cookies
        response.set_cookie('username', username)
        response.set_cookie('user_password', password)  # Passwort im Cookie!

        # Anfällig: Sensible Daten ohne Sicherheitsflags
        response.set_cookie('user_id', str(get_user_id(username)))
        response.set_cookie('user_role', get_user_role(username))

        return response

    return redirect('/login?error=1')

Korrigierter Code (Java/PHP)

// Korrigiert: Sichere Session-Handhabung ohne sensible Cookie-Daten
import javax.servlet.http.*;
import javax.crypto.*;
import java.security.SecureRandom;
import java.util.Base64;

public class SecureCookieStorage extends HttpServlet {

    private final SecretKey encryptionKey;
    private final Map<String, SessionData> serverSessions = new ConcurrentHashMap<>();

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) {

        String username = request.getParameter("username");

        // Korrigiert: Serverseitige Session erstellen
        String sessionId = generateSecureSessionId();
        serverSessions.put(sessionId, new SessionData(username));

        // Korrigiert: Nur Session-ID im Cookie speichern, nicht Account-Daten
        Cookie sessionCookie = new Cookie("session_id", sessionId);
        sessionCookie.setHttpOnly(true);   // Korrigiert: Kein JavaScript-Zugriff
        sessionCookie.setSecure(true);     // Korrigiert: Nur HTTPS
        sessionCookie.setMaxAge(3600);
        sessionCookie.setPath("/");

        // Korrigiert: SameSite-Attribut setzen (erfordert Servlet 4.0+ oder Header)
        response.setHeader("Set-Cookie",
            sessionCookie.getName() + "=" + sessionCookie.getValue() +
            "; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600");

        response.addCookie(sessionCookie);
    }

    // Korrigiert: Serverseitige Session-Abfrage
    public SessionData getSession(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return null;

        for (Cookie cookie : cookies) {
            if ("session_id".equals(cookie.getName())) {
                return serverSessions.get(cookie.getValue());
            }
        }
        return null;
    }

    // Korrigiert: Falls Cookie-Daten nötig, verschlüsseln
    public void setEncryptedCookie(HttpServletResponse response,
                                   String name, String value) {
        String encrypted = encrypt(value);

        Cookie cookie = new Cookie(name, encrypted);
        cookie.setHttpOnly(true);
        cookie.setSecure(true);
        cookie.setMaxAge(3600);
        response.addCookie(cookie);
    }

    private String encrypt(String data) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] iv = new byte[12];
            SecureRandom.getInstanceStrong().nextBytes(iv);
            cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(128, iv));
            byte[] encrypted = cipher.doFinal(data.getBytes());

            byte[] combined = new byte[iv.length + encrypted.length];
            System.arraycopy(iv, 0, combined, 0, iv.length);
            System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);

            return Base64.getUrlEncoder().encodeToString(combined);
        } catch (Exception e) {
            throw new RuntimeException("Verschlüsselung fehlgeschlagen", e);
        }
    }

    private String generateSecureSessionId() {
        byte[] bytes = new byte[32];
        new SecureRandom().nextBytes(bytes);
        return Base64.getUrlEncoder().encodeToString(bytes);
    }
}
<?php
// Korrigiert: Sichere Session-Handhabung
function secureLogin($userId) {
    // Korrigiert: PHP-Sessions anstelle von Credential-Cookies verwenden
    session_start();
    session_regenerate_id(true);  // Fixation verhindern

    // Korrigiert: Benutzerreferenz in serverseitiger Session speichern
    $_SESSION['user_id'] = $userId;
    $_SESSION['created_at'] = time();
    $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];

    // Korrigiert: Sichere Session-Cookie konfigurieren
    $cookieParams = [
        'lifetime' => 3600,
        'path' => '/',
        'secure' => true,      // Nur HTTPS
        'httponly' => true,    // Kein JavaScript-Zugriff
        'samesite' => 'Strict' // CSRF-Schutz
    ];

    session_set_cookie_params($cookieParams);

    // Keine sensiblen Daten in Cookies!
}

// Korrigiert: Verschlüsseltes Cookie falls Daten clientseitig gespeichert werden müssen
function setEncryptedCookie($name, $data) {
    $key = getEncryptionKey();  // Aus sicherem Speicher abrufen

    $iv = random_bytes(16);
    $encrypted = openssl_encrypt(
        json_encode($data),
        'AES-256-GCM',
        $key,
        0,
        $iv,
        $tag
    );

    $cookieValue = base64_encode($iv . $tag . $encrypted);

    setcookie(
        $name,
        $cookieValue,
        [
            'expires' => time() + 3600,
            'path' => '/',
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Strict'
        ]
    );
}

function getEncryptedCookie($name) {
    if (!isset($_COOKIE[$name])) {
        return null;
    }

    $key = getEncryptionKey();
    $data = base64_decode($_COOKIE[$name]);

    $iv = substr($data, 0, 16);
    $tag = substr($data, 16, 16);
    $encrypted = substr($data, 32);

    $decrypted = openssl_decrypt(
        $encrypted,
        'AES-256-GCM',
        $key,
        0,
        $iv,
        $tag
    );

    return json_decode($decrypted, true);
}
?>
# Korrigiert: Sichere Flask-Session-Handhabung
from flask import Flask, session, redirect, request, make_response
from flask_session import Session
import secrets

app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_bytes(32)
app.config['SESSION_TYPE'] = 'redis'  # Serverseitige Session
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'

Session(app)

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    if authenticate(username, password):
        # Korrigiert: Serverseitige Session-Speicherung
        session.regenerate()  # Fixation verhindern

        session['user_id'] = get_user_id(username)
        session['username'] = username
        session['authenticated'] = True
        session['created_at'] = time.time()

        # Korrigiert: Keine sensiblen Daten in Cookies
        # Nur Session-ID wird übertragen

        return redirect('/dashboard')

    return redirect('/login?error=1')

@app.route('/dashboard')
def dashboard():
    # Korrigiert: Session serverseitig verifizieren
    if not session.get('authenticated'):
        return redirect('/login')

    # Benutzerdaten aus serverseitiger Session holen
    user_id = session.get('user_id')

    return render_template('dashboard.html', user=get_user(user_id))

Die Korrektur verwendet serverseitige Sessions mit sicheren Session-Cookies und ordnungsgemäßen Sicherheitsflags.


Ausgenutzt in der Praxis

CVE-2002-1800 dokumentierte eine Webanwendung, die das Administrator-Passwort in einem Klartext-Cookie speicherte, was vollständigen administrativen Zugangsdiebstahl ermöglichte.

Standard-Konfiguration Credential-Exposition (Enterprise-Software, 2001)

CVE-2001-1537 und CVE-2001-1536 dokumentierten Produkte mit Standardkonfigurationen, die Benutzernamen und Passwörter in Klartext-Cookies speicherten.


Tools zum Testen/Ausnutzen

  • Browser Developer Tools — Integrierte Tools zum Anzeigen und Modifizieren von Cookies.

  • Burp Suite — Web-Sicherheitstool zum Abfangen und Analysieren von Cookies.

  • OWASP ZAP — Open-Source-Scanner, der unsichere Cookie-Einstellungen identifiziert.


CVE-Beispiele

  • CVE-2002-1800 — Admin-Passwort in Klartext-Cookie gespeichert.

  • CVE-2001-1537 — Klartext-Benutzernamen/Passwörter in Cookies.

  • CVE-2001-1536 — Benutzernamen/Passwörter in Klartext-Cookies.

  • CVE-2005-2160 — Authentifizierungsinformationen unverschlüsselt im Cookie gespeichert.


Referenzen

  1. MITRE Corporation. "CWE-315: Cleartext Storage of Sensitive Information in a Cookie." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/315.html

  2. OWASP Foundation. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html

  3. RFC 6265. "HTTP State Management Mechanism." https://tools.ietf.org/html/rfc6265