Sensibler Cookie mit unsachgemäßem SameSite-Attribut
Beschreibung
Sensibler Cookie mit unsachgemäßem SameSite-Attribut tritt auf, wenn das SameSite-Attribut für sensible Cookies nicht gesetzt ist oder ein unsicherer Wert verwendet wird. Das SameSite-Attribut steuert die domainubergreifende Cookie-Übertragung mit drei möglichen Werten: Lax, Strict oder None. Die Verwendung von "None" ohne zusätzliche Schutzmaßnahmen wie Anti-CSRF-Token ermöglicht potenzielle Cross-Site Request Forgery (CSRF)-Angriffe. Wenn SameSite nicht ordnungsgemäß konfiguriert ist, können Browser Cookies mit Cross-Site-Anfragen senden, was Angreifern potenziell ermöglicht, unbefugte Aktionen im Namen authentifizierter Benutzer durchzuführen.
Risiko
Unsachgemäße SameSite-Konfiguration hat erhebliche Sicherheitsauswirkungen. CSRF-Angriffe werden möglich. Session-Cookies können Cross-Site gesendet werden. Unbefugte Aktionen können durchgeführt werden. Benutzerkonten können kompromittiert werden. Finanztransaktionen können manipuliert werden. Daten können ohne Zustimmung modifiziert werden. Datenschutz kann verletzt werden. Kontoubernahme kann auftreten.
Lösung
Setzen Sie das SameSite-Attribut sensibler Cookies auf 'Lax' oder 'Strict'. Lax erlaubt Cookies für Top-Level-Navigation über sichere HTTP-Methoden (GET, HEAD, OPTIONS, TRACE), beschrankt aber zustandsändernde Anfragen. Strict bietet maximalen Schutz, kann aber die Benutzererfahrung beeinträchtigen. Implementieren Sie zusätzlich Anti-CSRF-Token für zustandsändernde Operationen unabhängig von der SameSite-Einstellung.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Umfang: Zugriffskontrolle Schutzmechanismus umgehen - CSRF-Schutzmaßnahmen können umgangen werden. |
| Integritat | Umfang: Integritat Anwendungsdaten ändern - Unbefugte Zustandsänderungen können auftreten. |
| Vertraulichkeit | Umfang: Vertraulichkeit Anwendungsdaten lesen - Sensible Operationen können ausgelöst werden. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Session-Cookie ohne SameSite-Attribut
const express = require('express');
const session = require('express-session');
const app = express();
// VERWUNDBAR: Kein SameSite-Attribut angegeben
app.use(session({
secret: 'session-secret',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: true
// VERWUNDBAR: SameSite nicht gesetzt
// Standardmäßig Browser-Verhalten (kann 'None' sein)
}
}));
// VERWUNDBAR: Zustandsändernde Aktion ohne CSRF-Schutz
app.post('/transfer', (req, res) => {
const { to, amount } = req.body;
// Cookie wird mit Cross-Site-Anfrage gesendet!
if (req.session.user) {
transferFunds(req.session.user, to, amount);
res.json({ success: true });
}
});
// Bosartige Seite des Angreifers kann Uberweisung auslosen:
// <form action="https://bank.com/transfer" method="POST">
// <input name="to" value="attacker">
// <input name="amount" value="10000">
// </form>
// <script>document.forms[0].submit();</script>
# VERWUNDBAR: Flask-Session ohne SameSite
from flask import Flask, session, request
app = Flask(__name__)
app.secret_key = 'secret-key'
# VERWUNDBAR: Keine SameSite-Konfiguration
app.config['SESSION_COOKIE_SAMESITE'] = None # Explizit deaktiviert!
app.config['SESSION_COOKIE_SECURE'] = True
@app.route('/change-email', methods=['POST'])
def change_email():
# VERWUNDBAR: Keine CSRF-Token-Validierung
if 'user_id' in session:
new_email = request.form.get('email')
# Session-Cookie wird mit Cross-Site-POST gesendet
update_user_email(session['user_id'], new_email)
return 'Email aktualisiert'
return 'Nicht autorisiert', 401
<?php
// VERWUNDBAR: PHP-Session-Cookie ohne SameSite
// VERWUNDBAR: Standard-Session-Cookie-Einstellungen
session_start();
// Oder explizit verwundbare Konfiguration
ini_set('session.cookie_samesite', ''); // Leer = kein SameSite
// VERWUNDBAR: Zustandsändernde Aktion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SESSION['user_id'])) {
// Kein CSRF-Schutz
$new_password = $_POST['password'];
// Cookie wird mit Cross-Site-Anfrage gesendet!
change_password($_SESSION['user_id'], $new_password);
echo "Passwort geändert";
}
// Seite des Angreifers löst Passwortwechsel aus
?>
// VERWUNDBAR: Java-Servlet-Cookie ohne SameSite
import javax.servlet.http.*;
public class VulnerableSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
// VERWUNDBAR: Session-Cookie ohne SameSite
HttpSession session = request.getSession(true);
// Cookie ohne SameSite-Attribut erstellen
Cookie sessionCookie = new Cookie("JSESSIONID", session.getId());
sessionCookie.setHttpOnly(true);
sessionCookie.setSecure(true);
// VERWUNDBAR: Kein SameSite gesetzt
response.addCookie(sessionCookie);
// Zustandsändernde Aktion ohne CSRF-Schutz
if (session.getAttribute("user") != null) {
String action = request.getParameter("action");
performAction(action); // Verwundbar für CSRF
}
}
}
Sichere Lösung
// SICHER: Session-Cookie mit ordnungsgemäßem SameSite-Attribut
const express = require('express');
const session = require('express-session');
const csrf = require('csurf');
const app = express();
// SICHER: SameSite-Attribut auf Strict gesetzt
app.use(session({
secret: 'session-secret',
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'strict', // SICHER: Striktes SameSite
maxAge: 3600000 // 1 Stunde
}
}));
// SICHER: Zusätzlicher CSRF-Schutz
const csrfProtection = csrf({ cookie: false });
app.use(csrfProtection);
// SICHER: Geschutzte zuständsändernde Aktion
app.post('/transfer', csrfProtection, (req, res) => {
const { to, amount } = req.body;
// SICHER: Cookie wird nicht Cross-Site gesendet dank SameSite=Strict
// SICHER: CSRF-Token wird ebenfalls verifiziert
if (req.session.user) {
transferFunds(req.session.user, to, amount);
res.json({ success: true });
}
});
// Für APIs die etwas Cross-Site-Funktionalitat benötigen
// SICHER: Lax mit zusätzlichem CSRF-Schutz verwenden
app.use('/api', session({
secret: 'api-session-secret',
cookie: {
httpOnly: true,
secure: true,
sameSite: 'lax', // SICHER: Lax für API-Kompatibilitat
}
}));
// SICHER: CSRF immer für zuständsändernde Operationen verifizieren
app.post('/api/action', csrfProtection, (req, res) => {
// Geschutzt durch sowohl SameSite=Lax als auch CSRF-Token
performAction(req.body);
});
# SICHER: Flask-Session mit ordnungsgemäßem SameSite
from flask import Flask, session, request
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.secret_key = 'secret-key'
# SICHER: CSRF-Schutz aktivieren
csrf = CSRFProtect(app)
# SICHER: SameSite=Strict konfigurieren
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
@app.route('/change-email', methods=['POST'])
def change_email():
# SICHER: CSRF-Token automatisch von Flask-WTF validiert
if 'user_id' in session:
new_email = request.form.get('email')
update_user_email(session['user_id'], new_email)
return 'Email aktualisiert'
return 'Nicht autorisiert', 401
# Alternative für APIs: Lax mit explizitem CSRF
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
@app.route('/api/action', methods=['POST'])
@csrf.exempt # Nur wenn anderer CSRF-Mechanismus verwendet wird
def api_action():
# SICHER: Benutzerdefinierten CSRF-Header für API verifizieren
csrf_header = request.headers.get('X-CSRF-Token')
if not verify_csrf_token(csrf_header, session.get('csrf_token')):
return 'CSRF-Validierung fehlgeschlagen', 403
# Aktion verarbeiten
return 'Erfolg'
<?php
// SICHER: PHP-Session-Cookie mit SameSite
// SICHER: SameSite vor Session-Start konfigurieren
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.cookie_secure', '1');
ini_set('session.cookie_httponly', '1');
session_start();
// SICHER: CSRF-Token generieren
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// SICHER: CSRF-Token für zuständsändernde Anfragen verifizieren
function verify_csrf_token($token) {
return isset($_SESSION['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $token);
}
// SICHER: Geschutzte zuständsändernde Aktion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SESSION['user_id'])) {
// SICHER: CSRF-Token verifizieren
$csrf_token = $_POST['csrf_token'] ?? '';
if (!verify_csrf_token($csrf_token)) {
http_response_code(403);
die('CSRF-Validierung fehlgeschlagen');
}
$new_password = $_POST['password'];
change_password($_SESSION['user_id'], $new_password);
echo "Passwort geändert";
}
// SICHER: CSRF-Token in Formulare einfugen
function csrf_field() {
return '<input type="hidden" name="csrf_token" value="' .
htmlspecialchars($_SESSION['csrf_token']) . '">';
}
?>
// SICHER: Java-Servlet-Cookie mit SameSite
import javax.servlet.http.*;
public class SecureSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
HttpSession session = request.getSession(true);
// SICHER: Session-Cookie mit SameSite=Strict setzen
String sessionId = session.getId();
String cookieValue = String.format(
"JSESSIONID=%s; HttpOnly; Secure; SameSite=Strict; Path=/",
sessionId
);
response.setHeader("Set-Cookie", cookieValue);
// SICHER: CSRF-Token verifizieren
String csrfToken = request.getHeader("X-CSRF-Token");
String sessionCsrf = (String) session.getAttribute("csrfToken");
if (csrfToken == null || !csrfToken.equals(sessionCsrf)) {
response.setStatus(403);
return;
}
// Zustandsändernde Aktion jetzt geschützt
if (session.getAttribute("user") != null) {
String action = request.getParameter("action");
performAction(action);
}
}
// SICHER: CSRF-Token für Session generieren
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
HttpSession session = request.getSession(true);
if (session.getAttribute("csrfToken") == null) {
String token = generateSecureToken();
session.setAttribute("csrfToken", token);
}
// Token in Antwort für Client-Verwendung einfugen
response.setHeader("X-CSRF-Token",
(String) session.getAttribute("csrfToken"));
}
}
CVE-Beispiele
SameSite-Cookie-Schwachstellen wurden in verschiedenen Webanwendungen gefunden, bei denen fehlende oder unsachgemäß konfigurierte SameSite-Attribute CSRF-Angriffe gegen authentifizierte Benutzer ermöglichten.
Verwandte CWEs
- CWE-923: Unsachgemäße Einschränkung des Kommunikationskanals auf vorgesehene Endpunkte (ubergeordnet)
- CWE-352: Cross-Site Request Forgery (kann vorausgehen)
- CWE-614: Sensibler Cookie in HTTPS-Sitzung ohne 'Secure'-Attribut (verwandt)
- CAPEC-62: Cross-Site Request Forgery (Angriffsmuster)
Referenzen
- MITRE Corporation. "CWE-1275: Sensitive Cookie with Improper SameSite Attribute." https://cwe.mitre.org/data/definitions/1275.html
- OWASP. "SameSite Cookie Attribute"
- RFC 6265bis. "Cookies: HTTP State Management Mechanism"