Vertrauen auf Cookies ohne Validierung und Integritätsprüfung
Beschreibung
Vertrauen auf Cookies ohne Validierung und Integritätsprüfung ist eine Schwachstelle, bei der eine Webanwendung sich auf die Existenz, das Format oder die Werte von Cookies für sicherheitskritische Operationen verlässt, ohne ordnungsgemäß zu verifizieren, dass diese Einstellungen gültig sind und nicht manipuliert wurden. Cookies werden auf der Client-Seite gespeichert und können von Angreifern mit Browser-Entwicklertools, Proxy-Interceptoren oder benutzerdefinierten HTTP-Clients leicht eingesehen, modifiziert oder gefälscht werden. Wenn Anwendungen Cookie-Werten für Authentifizierungsstatus, Benutzerrollen, Berechtigungen oder andere Sicherheitsentscheidungen ohne serverseitige Validierung oder Integritätsverifizierung vertrauen, können Angreifer diese Werte manipulieren, um Sicherheitskontrollen zu umgehen.
Risiko
Das Vertrauen auf nicht validierte Cookies erzeugt schwere Sicherheitsschwachstellen. Angreifer können Authentifizierungs-Cookies modifizieren, um Login-Anforderungen zu umgehen. Rollen- oder Berechtigungs-Cookies können geändert werden, um Privilegien vom normalen Benutzer zum Administrator zu eskalieren. Sitzungsdaten, die ohne Integritätsschutz in Cookies gespeichert sind, können manipuliert werden, um andere Benutzer zu imitieren. Warenkorb- oder Preisinformationen in Cookies können für Finanzbetrug modifiziert werden. Cookie-basierte Token ohne kryptografische Validierung können gefälscht werden. Die Leichtigkeit der Cookie-Manipulation bedeutet, dass diese Angriffe minimale Fähigkeiten erfordern, was sie hochgradig ausnutzbar macht. In Kombination mit anderen Schwachstellen kann Cookie-Manipulation Injection-Angriffe durch Einfügen bösartiger Werte ermöglichen.
Lösung
Vermeiden Sie die direkte Speicherung sicherheitskritischer Informationen in Cookies. Wenn Cookie-Daten verwendet werden müssen, implementieren Sie kryptografische Integritätsprüfungen mit HMAC oder authentifizierter Verschlüsselung, um Manipulationen zu erkennen. Speichern Sie sensiblen Zustand auf der Serverseite und verwenden Sie nur eine Sitzungskennung im Cookie. Validieren Sie alle Cookie-Daten gegen serverseitige Aufzeichnungen, bevor Sicherheitsentscheidungen getroffen werden. Implementieren Sie Replay-Schutz mit unvorhersehbaren, zeitlich begrenzten Token. Verwenden Sie die Secure- und HttpOnly-Cookie-Flags. Setzen Sie entsprechende SameSite-Attribute, um CSRF zu verhindern. Vertrauen Sie niemals clientseitigen Daten für Autorisierungsentscheidungen - verifizieren Sie immer gegen serverseitigen Zustand.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Anwendungsdaten ändern - Angreifer können Cookie-Werte manipulieren, um den Anwendungszustand zu ändern, Transaktionen zu modifizieren oder bösartigen Inhalt zu injizieren. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Privilegien erlangen oder Identität annehmen - Rollen- oder Berechtigungsinformationen, die in Cookies gespeichert sind, können modifiziert werden, um Privilegien auf Administratorebene zu eskalieren. |
| Verfügbarkeit | Bereich: Verfügbarkeit Schutzmechanismus umgehen - Authentifizierungs- und Autorisierungsmechanismen, die sich auf Cookie-Werte verlassen, können durch Cookie-Manipulation vollständig umgangen werden. |
Beispielcode
Verwundbarer Code
// Verwundbar: Vertrauen auf Cookie-Werte für Autorisierung
import javax.servlet.http.*;
public class VulnerableAuthServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Verwundbar: Rolle direkt aus Cookie lesen
Cookie[] cookies = request.getCookies();
String userRole = "guest";
if (cookies != null) {
for (Cookie c : cookies) {
if (c.getName().equals("role")) {
// Verwundbar: Cookie-Wert ohne Validierung vertrauen
userRole = c.getValue();
}
if (c.getName().equals("isAuthenticated")) {
// Verwundbar: Authentifizierungsstatus aus Cookie
if (c.getValue().equals("true")) {
// Benutzer gilt als authentifiziert nur basierend auf Cookie!
}
}
}
}
// Verwundbar: Autorisierung basierend auf nicht validiertem Cookie
if (userRole.equals("admin")) {
// Admin-Zugriff basierend auf leicht fälschbarem Cookie gewährt!
displayAdminPanel(response);
} else {
displayUserPanel(response);
}
}
// Verwundbar: Rolle in Cookie setzen
protected void doLogin(HttpServletRequest request, HttpServletResponse response,
String username, String password) {
if (authenticate(username, password)) {
// Verwundbar: Rolle im clientseitigen Cookie speichern
Cookie roleCookie = new Cookie("role", getUserRole(username));
Cookie authCookie = new Cookie("isAuthenticated", "true");
Cookie userIdCookie = new Cookie("userId", getUserId(username));
response.addCookie(roleCookie);
response.addCookie(authCookie);
response.addCookie(userIdCookie);
// Angreifer kann diese Cookies modifizieren um Privilegien zu eskalieren
}
}
}
<?php
// Verwundbar: PHP-Authentifizierung mit Cookies ohne Validierung
// Verwundbar: Login setzt ungeschützte Cookies
function vulnerable_login($username, $password) {
if (authenticate($username, $password)) {
$user = get_user($username);
// Verwundbar: Sensible Daten in Cookies speichern
setcookie('user_id', $user['id'], time() + 3600);
setcookie('username', $user['username'], time() + 3600);
setcookie('role', $user['role'], time() + 3600);
setcookie('is_admin', $user['is_admin'] ? '1' : '0', time() + 3600);
// Angreifer kann modifizieren: role=admin, is_admin=1
return true;
}
return false;
}
// Verwundbar: Autorisierung basierend auf Cookie-Werten
function check_admin_access() {
// Verwundbar: Cookie direkt vertrauen
if (isset($_COOKIE['is_admin']) && $_COOKIE['is_admin'] === '1') {
return true; // Admin-Zugriff basierend auf gefälschtem Cookie gewährt!
}
return false;
}
// Verwundbar: Benutzer aus Cookie abrufen
function get_current_user_id() {
// Verwundbar: Benutzer-ID aus Cookie kann modifiziert werden
return isset($_COOKIE['user_id']) ? $_COOKIE['user_id'] : null;
// Angreifer kann auf Daten anderer Benutzer zugreifen durch Ändern des user_id-Cookies
}
// Verwundbar: Preisberechnung vertraut Cookie
function calculate_total() {
// Verwundbar: Preis im Cookie gespeichert
$price = isset($_COOKIE['item_price']) ? $_COOKIE['item_price'] : 0;
$quantity = isset($_COOKIE['quantity']) ? $_COOKIE['quantity'] : 1;
// Angreifer kann item_price=0.01 im Cookie setzen!
return $price * $quantity;
}
?>
// Verwundbar: Node.js Express mit nicht validierten Cookies
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
// Verwundbar: Login setzt ungeschützte Cookies
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (await authenticate(username, password)) {
const user = await getUser(username);
// Verwundbar: Sensible Daten in Klartext-Cookies speichern
res.cookie('userId', user.id);
res.cookie('role', user.role);
res.cookie('permissions', JSON.stringify(user.permissions));
res.cookie('isAuthenticated', 'true');
res.redirect('/dashboard');
} else {
res.status(401).send('Ungültige Anmeldedaten');
}
});
// Verwundbar: Autorisierungs-Middleware vertraut Cookies
function requireAdmin(req, res, next) {
// Verwundbar: Rolle aus Cookie ohne Validierung
if (req.cookies.role === 'admin') {
next(); // Admin-Zugriff basierend auf gefälschtem Cookie!
} else {
res.status(403).send('Zugriff verweigert');
}
}
// Verwundbar: Benutzerdaten aus Cookie abrufen
app.get('/profile', (req, res) => {
// Verwundbar: Benutzer-ID aus Cookie kann manipuliert werden
const userId = req.cookies.userId;
// Angreifer kann auf jedes Benutzerprofil zugreifen durch Ändern des userId-Cookies
getUserProfile(userId).then(profile => res.json(profile));
});
// Verwundbar: Rabatt basierend auf Cookie
app.post('/checkout', (req, res) => {
// Verwundbar: Rabattprozentsatz aus Cookie
const discount = parseFloat(req.cookies.discount) || 0;
// Angreifer kann discount=100 im Cookie setzen für kostenlose Artikel!
const total = calculateTotal(req.body.items) * (1 - discount / 100);
processPayment(total);
});
Lösungscode
// Behoben: Serverseitige Sitzung mit signierten Cookies
import javax.servlet.http.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class SecureAuthServlet extends HttpServlet {
private static final String HMAC_SECRET = System.getenv("COOKIE_SECRET");
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Behoben: Sitzung aus serverseitigem Speicher abrufen
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("userId") == null) {
response.sendRedirect("/login");
return;
}
// Behoben: Rolle aus serverseitiger Sitzung, nicht aus Cookie
String userRole = (String) session.getAttribute("role");
if ("admin".equals(userRole)) {
displayAdminPanel(response);
} else {
displayUserPanel(response);
}
}
protected void doLogin(HttpServletRequest request, HttpServletResponse response,
String username, String password) throws IOException {
if (authenticate(username, password)) {
// Behoben: Daten in serverseitiger Sitzung speichern
HttpSession session = request.getSession(true);
session.setAttribute("userId", getUserId(username));
session.setAttribute("username", username);
session.setAttribute("role", getUserRole(username));
// Behoben: Nur Sitzungs-ID im Cookie (vom Container verwaltet)
// Sitzungs-ID ist in modernen Containern standardmäßig HttpOnly
response.sendRedirect("/dashboard");
}
}
// Behoben: Signiertes Cookie für zuständslose Daten
private void setSignedCookie(HttpServletResponse response, String name, String value) {
String signature = computeHmac(value);
String signedValue = value + "." + signature;
Cookie cookie = new Cookie(name, signedValue);
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
response.addCookie(cookie);
}
private String getVerifiedCookieValue(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies == null) return null;
for (Cookie c : cookies) {
if (c.getName().equals(name)) {
String[] parts = c.getValue().split("\\.");
if (parts.length != 2) return null;
String value = parts[0];
String signature = parts[1];
// Behoben: Signatur verifizieren
if (computeHmac(value).equals(signature)) {
return value;
}
}
}
return null;
}
private String computeHmac(String data) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(HMAC_SECRET.getBytes(), "HmacSHA256"));
return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes()));
} catch (Exception e) {
throw new RuntimeException("HMAC-Berechnung fehlgeschlagen", e);
}
}
}
<?php
// Behoben: PHP mit serverseitigen Sitzungen und signierten Cookies
session_start();
// Konfiguration
define('COOKIE_SECRET', getenv('COOKIE_SECRET'));
// Behoben: Login mit serverseitigen Sitzungen
function secure_login($username, $password) {
if (authenticate($username, $password)) {
$user = get_user($username);
// Behoben: Sensible Daten in serverseitiger Sitzung speichern
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
$_SESSION['is_admin'] = $user['is_admin'];
$_SESSION['login_time'] = time();
// Behoben: Sitzungs-ID regenerieren um Fixierung zu verhindern
session_regenerate_id(true);
return true;
}
return false;
}
// Behoben: Autorisierung basierend auf serverseitiger Sitzung
function check_admin_access() {
// Behoben: Sitzung prüfen, nicht Cookie
return isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
}
// Behoben: Benutzer aus Sitzung abrufen
function get_current_user_id() {
// Behoben: Benutzer-ID aus serverseitiger Sitzung
return isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
}
// Behoben: Signiertes Cookie für zuständslose Daten (falls benötigt)
function set_signed_cookie($name, $value, $expiry) {
$signature = hash_hmac('sha256', $value, COOKIE_SECRET);
$signed_value = base64_encode($value) . '.' . $signature;
setcookie($name, $signed_value, [
'expires' => $expiry,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
}
function get_verified_cookie($name) {
if (!isset($_COOKIE[$name])) {
return null;
}
$parts = explode('.', $_COOKIE[$name]);
if (count($parts) !== 2) {
return null;
}
$value = base64_decode($parts[0]);
$signature = $parts[1];
// Behoben: Signatur mit konstantem Zeitvergleich verifizieren
$expected = hash_hmac('sha256', $value, COOKIE_SECRET);
if (hash_equals($expected, $signature)) {
return $value;
}
return null; // Manipuliertes Cookie
}
// Behoben: Warenkorb mit serverseitigem Speicher
function add_to_cart($product_id, $quantity) {
// Behoben: Preis vom Server holen, nicht vom Client
$product = get_product($product_id);
if (!$product) return false;
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
}
$_SESSION['cart'][$product_id] = [
'quantity' => $quantity,
'price' => $product['price'] // Serverseitiger Preis
];
return true;
}
function calculate_total() {
// Behoben: Aus serverseitigen Sitzungsdaten berechnen
$total = 0;
if (isset($_SESSION['cart'])) {
foreach ($_SESSION['cart'] as $item) {
$total += $item['price'] * $item['quantity'];
}
}
return $total;
}
?>
// Behoben: Node.js Express mit signierten Sitzungen
const express = require('express');
const session = require('express-session');
const crypto = require('crypto');
const app = express();
// Behoben: Signierte Sitzungen verwenden
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // Nur HTTPS
httpOnly: true, // Kein JavaScript-Zugriff
sameSite: 'strict', // CSRF-Schutz
maxAge: 3600000 // 1 Stunde
}
}));
// Behoben: Login speichert Daten in serverseitiger Sitzung
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (await authenticate(username, password)) {
const user = await getUser(username);
// Behoben: In serverseitiger Sitzung speichern
req.session.userId = user.id;
req.session.username = user.username;
req.session.role = user.role;
req.session.permissions = user.permissions;
req.session.loginTime = Date.now();
// Behoben: Sitzungs-ID regenerieren
req.session.regenerate((err) => {
if (err) {
return res.status(500).send('Sitzungsfehler');
}
res.redirect('/dashboard');
});
} else {
res.status(401).send('Ungültige Anmeldedaten');
}
});
// Behoben: Autorisierungs-Middleware verwendet Sitzung
function requireAdmin(req, res, next) {
// Behoben: Rolle aus serverseitiger Sitzung
if (req.session && req.session.role === 'admin') {
next();
} else {
res.status(403).send('Zugriff verweigert');
}
}
// Behoben: Benutzeridentifikation aus Sitzung
app.get('/profile', (req, res) => {
if (!req.session.userId) {
return res.status(401).send('Nicht authentifiziert');
}
// Behoben: Benutzer-ID aus serverseitiger Sitzung
getUserProfile(req.session.userId)
.then(profile => res.json(profile));
});
// Behoben: Rabatt serverseitig angewendet
app.post('/checkout', async (req, res) => {
if (!req.session.userId) {
return res.status(401).send('Nicht authentifiziert');
}
// Behoben: Rabatt aus serverseitigen Benutzerdaten holen
const user = await getUser(req.session.userId);
const discount = user.discountPercentage || 0;
const total = calculateTotal(req.body.items) * (1 - discount / 100);
await processPayment(req.session.userId, total);
res.json({ total });
});
// Behoben: Signierter Cookie-Helfer für zuständslose Daten
function signedCookie(name, value, secret) {
const signature = crypto
.createHmac('sha256', secret)
.update(value)
.digest('base64');
return `${Buffer.from(value).toString('base64')}.${signature}`;
}
function verifySignedCookie(cookieValue, secret) {
const [encodedValue, signature] = cookieValue.split('.');
if (!encodedValue || !signature) return null;
const value = Buffer.from(encodedValue, 'base64').toString();
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(value)
.digest('base64');
// Behoben: Konstantzeit-Vergleich
if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
return value;
}
return null;
}
module.exports = app;
CVE-Beispiele
- CVE-2016-4437: Apache Shiro Cookie-basierte RememberMe-Schwachstelle ermöglichte Angreifern die Umgehung der Authentifizierung.
- CVE-2015-8562: Joomla Sitzungs-Cookie-Schwachstelle ermöglichte Remote-Code-Ausführung.
- CVE-2012-3137: Oracle HTTP Server Cookie-Manipulationsschwachstelle.
Referenzen
- MITRE Corporation. "CWE-565: Reliance on Cookies without Validation and Integrity Checking." https://cwe.mitre.org/data/definitions/565.html
- OWASP. "Session Management Cheat Sheet."
- OWASP. "Testing for Cookies Attributes."