Verwendung von Webbrowser-Cache mit sensiblen Informationen

Beschreibung

Verwendung von Webbrowser-Cache mit sensiblen Informationen tritt auf, wenn eine Webanwendung die Speicherung sensibler Daten im Browser-Cache, Verlauf oder clientseitigen Speichermechanismen erlaubt. Dies umfasst das Cachen von Seiten mit persönlichen Informationen, das Speichern sensibler Daten in localStorage/sessionStorage, das Erlauben von Autocomplete bei sensiblen Formularfeldern und das Versäumnis, angemessene Cache-Control-Header zu setzen. Sensible Daten bleiben auf Client-Geräten bestehen, wo sie von anderen Benutzern oder Malware zugänglich sind.

Risiko

Gecachte sensible Informationen erzeugen mehrere Angriffsvektoren. Auf geteilten Computern (Bibliotheken, Kiosks, Familiengeräte) können nachfolgende Benutzer auf gecachte Daten vorheriger Benutzer zugreifen. Der Browserverlauf legt sensible URLs offen. Autocomplete enthüllt vorherige Formulareingaben einschließlich Passwörter und Kreditkarten. localStorage bleibt auch nach Sitzungsende bestehen. Gecachte Antworten können über Browser-Entwicklertools oder Dateisystem-Inspektion zugegriffen werden. Dieses Risiko wird für Anwendungen verstärkt, die Finanz-, Medizin- oder persönliche Daten verarbeiten.

Lösung

Setzen Sie angemessene Cache-Control-Header, um das Cachen sensibler Seiten zu verhindern: Cache-Control: no-store, no-cache, must-revalidate, private. Fügen Sie Pragma: no-cache für HTTP/1.0-Kompatibilität hinzu. Deaktivieren Sie Autocomplete bei sensiblen Formularfeldern mit autocomplete="off" oder spezifischen Werten wie autocomplete="new-password". Vermeiden Sie das Speichern sensibler Daten in localStorage/sessionStorage - verwenden Sie stattdessen httpOnly-Cookies oder serverseitige Sitzungen. Löschen Sie sensible Daten bei Benutzerabmeldung. Verwenden Sie nur-Speicher-Speicherung für sensible clientseitige Daten.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Informationsoffenlegung

Sensible Daten für nachfolgende Benutzer geteilter Geräte zugänglich.
DatenschutzBereich: Persönliche Datenoffenlegung

PII, Finanzdaten und Krankenakten durch Caching offengelegt.
AuthentifizierungBereich: Anmeldedatenoffenlegung

Gecachte Anmeldedaten oder Sitzungsdaten ermöglichen unbefugten Zugriff.

Beispielcode + Lösungscode

Verwundbarer Code

# VERWUNDBAR: Flask ohne Cache-Control-Header
from flask import Flask, render_template, jsonify

@app.route('/account/dashboard')
def dashboard_vulnerable():
    user = get_current_user()
    # Sensible Seite kann vom Browser gecacht werden!
    return render_template('dashboard.html', user=user)

@app.route('/api/user/profile')
def profile_vulnerable():
    user = get_current_user()
    # API-Antwort mit PII kann gecacht werden!
    return jsonify({
        'name': user.name,
        'ssn': user.ssn,
        'account_number': user.account_number
    })

# VERWUNDBAR: Sensible Daten in Antwort-URL
@app.route('/receipt')
def receipt_vulnerable():
    # Sensible Daten in URL - im Verlauf gespeichert!
    return redirect(f'/receipt/view?ssn={ssn}&amount={amount}')
// VERWUNDBAR: Servlet ohne Cache-Header
@WebServlet("/account/details")
public class VulnerableServlet extends HttpServlet {

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

        // Keine Cache-Control-Header!
        User user = getCurrentUser(request);

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<p>SSN: " + user.getSsn() + "</p>");
        out.println("<p>Konto: " + user.getAccountNumber() + "</p>");
        out.println("</body></html>");
    }
}

// VERWUNDBAR: Spring-Controller
@Controller
public class VulnerableController {

    @GetMapping("/transactions")
    public String transactions(Model model) {
        // Antwort standardmäßig gecacht!
        model.addAttribute("transactions", getTransactions());
        return "transactions";
    }
}
<!-- VERWUNDBAR: HTML-Formular ohne Autocomplete-Kontrolle -->
<form action="/login" method="POST">
    <!-- Browser-Autocomplete standardmäßig aktiviert! -->
    <input type="text" name="username" placeholder="Benutzername">
    <input type="password" name="password" placeholder="Passwort">

    <!-- Kreditkarte kann gespeichert werden! -->
    <input type="text" name="cardNumber" placeholder="Kartennummer">
    <input type="text" name="cvv" placeholder="CVV">

    <!-- SSN könnte gemerkt werden! -->
    <input type="text" name="ssn" placeholder="Sozialversicherungsnummer">

    <button type="submit">Absenden</button>
</form>
// VERWUNDBAR: Sensible Daten in localStorage speichern
function loginVulnerable(username, password) {
    // Anmeldedaten dauerhaft gespeichert!
    localStorage.setItem('username', username);
    localStorage.setItem('password', password);  // NIEMALS tun!
    localStorage.setItem('authToken', getToken());
}

// VERWUNDBAR: Sensible Daten in sessionStorage
function loadUserProfile(user) {
    // PII im Browser-Speicher!
    sessionStorage.setItem('user', JSON.stringify({
        ssn: user.ssn,
        accountNumber: user.accountNumber,
        dateOfBirth: user.dob
    }));
}

// VERWUNDBAR: Daten bei Abmeldung nicht löschen
function logoutVulnerable() {
    // Löscht nur Sitzung, lässt gecachte Daten!
    fetch('/api/logout', { method: 'POST' });
    window.location = '/login';
    // localStorage/sessionStorage-Daten bleiben bestehen!
}

// VERWUNDBAR: Express ohne Cache-Header
app.get('/api/sensitive-data', (req, res) => {
    // Keine Cache-Kontrolle!
    res.json({
        ssn: req.user.ssn,
        accounts: req.user.accounts
    });
});

Lösungscode

# SICHER: Flask mit Cache-Control-Headern
from flask import Flask, render_template, jsonify, make_response

def add_no_cache_headers(response):
    """Header hinzufügen um Caching sensibler Daten zu verhindern."""
    response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, private, max-age=0'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    return response

@app.route('/account/dashboard')
def dashboard_safe():
    user = get_current_user()
    response = make_response(render_template('dashboard.html', user=user))
    return add_no_cache_headers(response)

@app.route('/api/user/profile')
def profile_safe():
    user = get_current_user()
    response = jsonify({
        'name': user.name,
        'ssn': user.ssn,
        'account_number': user.account_number
    })
    return add_no_cache_headers(response)

# SICHER: Auf alle sensiblen Routen anwenden
@app.after_request
def add_security_headers(response):
    """Sicherheits-Header zu allen Antworten hinzufügen."""
    # Für authentifizierte Seiten
    if hasattr(g, 'current_user') and g.current_user:
        response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, private'
        response.headers['Pragma'] = 'no-cache'
        response.headers['Expires'] = '0'

    return response

# SICHER: POST für sensible Operationen (wird nicht gecacht)
@app.route('/receipt', methods=['POST'])
def receipt_safe():
    # POST-Anfragen werden von Browsern nicht gecacht
    # Sensible Daten im POST-Body, nicht URL
    ssn = request.form['ssn']
    amount = request.form['amount']
    return render_receipt(ssn, amount)
// SICHER: Servlet mit Cache-Headern
@WebServlet("/account/details")
public class SecureServlet extends HttpServlet {

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

        // Cache-Control-Header setzen
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, private");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);

        User user = getCurrentUser(request);

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<p>SSN: " + user.getSsn() + "</p>");
        out.println("<p>Konto: " + user.getAccountNumber() + "</p>");
        out.println("</body></html>");
    }
}

// SICHER: Spring-Controller mit Cache-Kontrolle
@Controller
public class SecureController {

    @GetMapping("/transactions")
    public ResponseEntity<String> transactions() {
        return ResponseEntity.ok()
            .cacheControl(CacheControl.noStore())
            .header("Pragma", "no-cache")
            .header("Expires", "0")
            .body(renderTransactions());
    }
}

// SICHER: Spring Security Konfiguration für Cache-Kontrolle
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .cacheControl(cache -> {})  // Fügt no-cache, no-store, etc. hinzu
                .contentTypeOptions(content -> {})
            );

        return http.build();
    }
}

// SICHER: Filter für alle sensiblen Endpunkte
@WebFilter("/secure/*")
public class NoCacheFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletResponse response = (HttpServletResponse) res;

        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, private");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);

        chain.doFilter(req, res);
    }
}
<!-- SICHER: HTML-Formular mit Autocomplete-Kontrollen -->
<form action="/login" method="POST" autocomplete="off">
    <!-- Spezifische Autocomplete-Werte -->
    <input type="text" name="username" autocomplete="username">

    <!-- Passwortspeicherung verhindern -->
    <input type="password" name="password" autocomplete="new-password">

    <!-- Finanzfelder - Autocomplete verhindern -->
    <input type="text" name="cardNumber" autocomplete="off"
           inputmode="numeric" pattern="[0-9]*">
    <input type="text" name="cvv" autocomplete="off"
           inputmode="numeric" maxlength="4">

    <!-- Sensible PII - kein Autocomplete -->
    <input type="text" name="ssn" autocomplete="off"
           inputmode="numeric" pattern="[0-9-]*">

    <button type="submit">Absenden</button>
</form>

<!-- Für Einmalcodes -->
<input type="text" name="otp" autocomplete="one-time-code">
// SICHER: Sichere clientseitige Speichermuster
class SecureStorage {
    constructor() {
        // Speicher-Speicher für sensible Daten verwenden
        this.sensitiveData = new Map();
    }

    // Sensible Daten nur im Speicher ablegen
    setSecure(key, value) {
        this.sensitiveData.set(key, value);
    }

    getSecure(key) {
        return this.sensitiveData.get(key);
    }

    // Alle sensiblen Daten löschen
    clearAll() {
        this.sensitiveData.clear();
    }
}

const secureStorage = new SecureStorage();

// SICHER: Ordnungsgemäße Abmeldung mit Datenlöschung
function logoutSafe() {
    // Alle clientseitigen Daten löschen
    localStorage.clear();
    sessionStorage.clear();
    secureStorage.clearAll();

    // Cookies über Server löschen
    fetch('/api/logout', {
        method: 'POST',
        credentials: 'include'
    }).then(() => {
        // Seitenneuladen erzwingen um Speicher zu löschen
        window.location.href = '/login';
    });
}

// SICHER: Sensible Daten bei Seitenentladen löschen
window.addEventListener('beforeunload', () => {
    // Sensible In-Memory-Daten löschen
    secureStorage.clearAll();
});

// SICHER: Express mit Cache-Kontrolle
const helmet = require('helmet');

app.use(helmet.noSniff());

// Middleware für sensible Routen
const noCacheMiddleware = (req, res, next) => {
    res.set({
        'Cache-Control': 'no-store, no-cache, must-revalidate, private',
        'Pragma': 'no-cache',
        'Expires': '0',
        'Surrogate-Control': 'no-store'
    });
    next();
};

app.use('/api/sensitive', noCacheMiddleware);
app.use('/account', noCacheMiddleware);

app.get('/api/sensitive-data', noCacheMiddleware, (req, res) => {
    res.json({
        ssn: req.user.ssn,
        accounts: req.user.accounts
    });
});

// SICHER: Keine sensiblen Daten clientseitig speichern
// Stattdessen httpOnly-Cookies für Sitzungstokens verwenden
app.post('/login', (req, res) => {
    const token = generateToken(req.body.username);

    // Token in httpOnly-Cookie speichern - nicht für JS zugänglich
    res.cookie('session', token, {
        httpOnly: true,
        secure: true,
        sameSite: 'strict',
        maxAge: 3600000
    });

    // Keine sensiblen Daten an Client zurückgeben
    res.json({ success: true });
});

// SICHER: Sitzungsbasierter Datenabruf
app.get('/api/profile', requireAuth, (req, res) => {
    // Daten jedes Mal vom Server abgerufen, nicht gecacht
    // Benutzer-ID aus sicherer Sitzung, nicht Client-Speicher
    const user = getUserFromSession(req);

    res.set('Cache-Control', 'no-store');
    res.json({
        name: user.name,
        // Nur zurückgeben was benötigt wird
    });
});

Ausgenutzt in der Praxis

Geteilte Computer-Informationsleck

Öffentliche Bibliotheks- und Internetcafé-Computer haben Bank- und persönliche Informationen vorheriger Benutzer durch Browser-Cache offengelegt.

Browserverlauf-Offenlegung

Sensible URLs mit Tokens, IDs oder Parametern legten Benutzerdaten durch Browserverlauf offen.

Formular-Autofill-Angriffe

Bösartige Websites haben Browser-Autofill ausgenutzt, um Kreditkartennummern und Passwörter aus gecachten Formulardaten zu stehlen.


Tools zum Testen/Ausnutzen

  • Browser-Entwicklertools — Cache, localStorage, sessionStorage inspizieren.

  • Burp Suite — Cache-Header analysieren.

  • OWASP ZAP — Auf Caching-Probleme testen.

  • SecurityHeaders.com — Header-Konfiguration prüfen.


CVE-Beispiele

  • CVE-2019-11358 — Cache-bezogene Informationsoffenlegung.

  • CVE-2020-8945 — Schwachstelle beim Caching sensibler Daten.

  • Zahlreiche anwendungsspezifische Caching-Probleme.


Referenzen

  1. MITRE. "CWE-525: Use of Web Browser Cache Containing Sensitive Information." https://cwe.mitre.org/data/definitions/525.html

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