Authentifizierungsumgehung durch als unveränderlich angenommene Daten

Beschreibung

Authentifizierungsumgehung durch als unveränderlich angenommene Daten ist eine Schwachstelle, die auftritt, wenn ein Authentifizierungsschema Schlüsseldatenelemente verwendet, von denen Entwickler fälschlicherweise annehmen, dass sie nicht von Angreifern kontrolliert oder geändert werden können. Gängige Beispiele umfassen das Vertrauen auf clientseitige Cookies, versteckte Formularfelder, Umgebungsvariablen oder HTTP-Header für Authentifizierungsentscheidungen. Obwohl diese Mechanismen aus Sicht eines legitimen Benutzers schwer zu ändern erscheinen mögen, können Angreifer sie leicht mit Browser-Entwicklertools, Proxy-Interceptors oder benutzerdefinierten HTTP-Clients manipulieren. Daten, die vom Client stammen oder durch den Client geleitet werden, können nicht ohne serverseitige Validierung für Sicherheitsentscheidungen vertraut werden.

Risiko

Das Vertrauen auf als unveränderlich angenommene Daten für die Authentifizierung erzeugt trivial ausnutzbare Schwachstellen. Angreifer können leicht verfügbare Tools wie Browser-Entwicklerkonsolen, HTTP-Proxy-Interceptors oder einfache Befehlszeilen-Dienstprogramme verwenden, um Cookies, versteckte Felder und Header zu modifizieren. Das Risiko ist schwerwiegend, da die Ausnutzung minimale technische Fähigkeiten erfordert, aber eine vollständige Authentifizierungsumgehung gewährt. Reale Exploits haben gezeigt, dass Angreifer administrativen Zugang einfach durch Setzen eines Cookie-Werts auf "true" oder Ändern eines versteckten Formularfelds erlangen. Diese Schwachstellen bleiben oft unerkannt, da sie während normaler Tests korrekt funktionieren -- nur gezielte Manipulation offenbart den Fehler. Die Auswirkungen reichen von unautorisiertem Datenzugriff bis zur vollständigen Systemkompromittierung, abhängig von der Zugriffsebene, die durch den verwundbaren Mechanismus kontrolliert wird.

Lösung

Vertrauen Sie niemals clientseitig bereitgestellten Daten für Authentifizierungsentscheidungen ohne serverseitige Verifizierung. Speichern Sie den Authentifizierungsstatus ausschließlich serverseitig unter Verwendung kryptographisch sicherer Session-Identifikatoren. Wenn clientseitige Tokens verwendet werden müssen, signieren Sie sie kryptographisch (z.B. mit HMAC oder JWT mit Signaturen) und verifizieren Sie Signaturen serverseitig, bevor Sie Angaben vertrauen. Implementieren Sie ordnungsgemäßes Session-Management mit serverseitigen Session-Speichern. Validieren und bereinigen Sie alle Eingaben von Clients und behandeln Sie versteckte Formularfelder, Cookies und Header mit demselben Misstrauen wie sichtbare Benutzereingaben. Verwenden Sie vom Framework bereitgestellte Authentifizierungsmechanismen anstelle eigener Lösungen. Wenden Sie Defense-in-Depth an -- selbst wenn eine Schicht umgangen wird, sollten zusätzliche Prüfungen unautorisierten Zugang verhindern.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Angreifer können die Authentifizierung vollständig umgehen, indem sie als unveränderlich angenommene Daten modifizieren, und erhalten unautorisierten Zugang als beliebiger Benutzer einschließlich Administratoren.
Integrität, VertraulichkeitBereich: Integrität, Vertraulichkeit

Bei umgangener Authentifizierung können Angreifer auf sensible Daten zugreifen, Datensätze ändern und jede Aktion ausführen, die der imitierte Benutzer ausführen könnte.

Beispielcode und Lösung

Verwundbarer Code (Java)

Die folgenden Beispiele zeigen Authentifizierungsumgehung durch als unveränderlich angenommene Daten:

// Verwundbar: Vertrauen auf Cookie-Wert für Authentifizierung
import javax.servlet.http.*;

public class VulnerableAuthServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) {

        // Verwundbar: Authentifizierungsstatus aus Cookie lesen
        Cookie[] cookies = request.getCookies();
        boolean authenticated = false;
        String role = "user";

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                // Verwundbar: Vertrauen auf clientkontrollierten Cookie
                if ("authenticated".equals(cookie.getName())) {
                    authenticated = Boolean.parseBoolean(cookie.getValue());
                }
                if ("role".equals(cookie.getName())) {
                    role = cookie.getValue();  // Angreifer setzt auf "admin"
                }
            }
        }

        // Angreifer setzt einfach den Cookie authenticated=true
        if (authenticated) {
            if ("admin".equals(role)) {
                // Voller Admin-Zugang nur durch Cookie-Änderung!
                showAdminPanel(response);
            } else {
                showUserDashboard(response);
            }
        } else {
            redirectToLogin(response);
        }
    }
}
# Verwundbar: Vertrauen auf verstecktes Formularfeld für Autorisierung
from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/transfer', methods=['POST'])
def transfer_funds():
    # Verwundbar: Benutzer-ID aus verstecktem Formularfeld
    user_id = request.form.get('user_id')  # Verstecktes Feld im Formular
    amount = float(request.form.get('amount'))
    target_account = request.form.get('target_account')

    # Angreifer ändert versteckte user_id, um einen anderen Benutzer zu imitieren
    # Keine serverseitige Verifizierung des tatsächlich angemeldeten Benutzers

    perform_transfer(user_id, target_account, amount)
    return "Transfer completed"

@app.route('/admin', methods=['GET'])
def admin_panel():
    # Verwundbar: Vertrauen auf Query-Parameter für Admin-Prüfung
    is_admin = request.args.get('admin', 'false')

    # Angreifer besucht einfach /admin?admin=true
    if is_admin.lower() == 'true':
        return render_template('admin_panel.html')
    else:
        return "Access Denied", 403
<?php
// Verwundbar: Vertrauen auf mehrere als unveränderlich angenommene Quellen

// Verwundbar: Cookie-basierte Authentifizierung
$authenticated = isset($_COOKIE['logged_in']) && $_COOKIE['logged_in'] === 'true';
$username = $_COOKIE['username'] ?? '';

// Verwundbar: Verstecktes Formularfeld für Autorisierung
$user_level = $_POST['user_level'] ?? 'guest';

// Verwundbar: HTTP-Header für Admin-Umgehung
$is_admin = isset($_SERVER['HTTP_X_ADMIN']) && $_SERVER['HTTP_X_ADMIN'] === 'true';

if (!$authenticated) {
    header('Location: /login.php');
    exit;
}

// Angreifer setzt X-Admin: true Header und erhält Admin-Zugang
if ($is_admin || $user_level === 'admin') {
    include 'admin_dashboard.php';
} else {
    include 'user_dashboard.php';
}
?>

Sichere Lösung (Java)

// Behoben: Serverseitiges Session-Management für Authentifizierung
import javax.servlet.http.*;
import java.security.SecureRandom;
import java.util.*;

public class SecureAuthServlet extends HttpServlet {

    // Serverseitiger Session-Speicher
    private static final Map<String, UserSession> sessions =
        new ConcurrentHashMap<>();

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) {

        // Behoben: Session-ID aus Cookie holen, serverseitig validieren
        String sessionId = null;
        Cookie[] cookies = request.getCookies();

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("session_id".equals(cookie.getName())) {
                    sessionId = cookie.getValue();
                    break;
                }
            }
        }

        // Behoben: Session-Daten serverseitig nachschlagen
        UserSession session = (sessionId != null) ? sessions.get(sessionId) : null;

        // Behoben: Prüfen, ob Session existiert und nicht abgelaufen ist
        if (session == null || session.isExpired()) {
            redirectToLogin(response);
            return;
        }

        // Behoben: Rolle aus serverseitiger Session holen, nicht vom Client
        if ("admin".equals(session.getRole())) {
            showAdminPanel(response);
        } else {
            showUserDashboard(response);
        }
    }

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // Gegen Datenbank authentifizieren
        User user = authenticateUser(username, password);

        if (user != null) {
            // Behoben: Serverseitige Session erstellen
            String sessionId = generateSecureSessionId();
            UserSession session = new UserSession(user.getId(), user.getRole());
            sessions.put(sessionId, session);

            // Nur Session-ID an Client senden (nicht den Auth.-Status)
            Cookie sessionCookie = new Cookie("session_id", sessionId);
            sessionCookie.setHttpOnly(true);
            sessionCookie.setSecure(true);
            sessionCookie.setMaxAge(3600);
            response.addCookie(sessionCookie);

            response.sendRedirect("/dashboard");
        } else {
            response.sendRedirect("/login?error=invalid");
        }
    }

    private String generateSecureSessionId() {
        byte[] bytes = new byte[32];
        new SecureRandom().nextBytes(bytes);
        return Base64.getUrlEncoder().encodeToString(bytes);
    }
}
# Behoben: Serverseitiges Session-Management
from flask import Flask, request, session, redirect, url_for
from functools import wraps
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_bytes(32)

# Serverseitiger Session-Speicher (in Produktion Redis oder Datenbank verwenden)
user_sessions = {}

def login_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        # Behoben: Session serverseitig verifizieren
        session_id = session.get('session_id')
        if not session_id or session_id not in user_sessions:
            return redirect(url_for('login'))

        # Behoben: Benutzerinfo aus serverseitigem Speicher holen
        user_data = user_sessions[session_id]
        if user_data.get('expired'):
            return redirect(url_for('login'))

        return f(user_data, *args, **kwargs)
    return decorated

def admin_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        session_id = session.get('session_id')
        if not session_id or session_id not in user_sessions:
            return redirect(url_for('login'))

        user_data = user_sessions[session_id]

        # Behoben: Rolle serverseitig geprüft, nicht vom Client
        if user_data.get('role') != 'admin':
            return "Access Denied", 403

        return f(user_data, *args, **kwargs)
    return decorated

@app.route('/transfer', methods=['POST'])
@login_required
def transfer_funds(user_data):
    # Behoben: user_id aus serverseitiger Session holen, nicht aus Formular
    user_id = user_data['user_id']  # Aus authentifizierter Session
    amount = float(request.form.get('amount'))
    target_account = request.form.get('target_account')

    # Jetzt mit verifizierter user_id vom Server
    perform_transfer(user_id, target_account, amount)
    return "Transfer completed"

@app.route('/admin')
@admin_required
def admin_panel(user_data):
    # Behoben: Rolle vor Erreichen dieses Punktes serverseitig verifiziert
    return render_template('admin_panel.html')

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    user = authenticate_user(username, password)
    if user:
        # Behoben: Serverseitige Session erstellen
        session_id = secrets.token_urlsafe(32)
        user_sessions[session_id] = {
            'user_id': user.id,
            'role': user.role,  # Rolle aus Datenbank
            'created_at': time.time()
        }
        session['session_id'] = session_id
        return redirect(url_for('dashboard'))

    return redirect(url_for('login', error='invalid'))

Die Behebung speichert den Authentifizierungsstatus serverseitig und sendet nur einen kryptographisch zufälligen Session-Identifikator an den Client.


Ausgenutzt in der Praxis

CVE-2002-1730 und CVE-2002-1734 dokumentierten Webanwendungen, die administrativen Zugang gewährten, wenn Benutzer Authentifizierungs-Cookies auf "true" setzten, was eine triviale Umgehung der Authentifizierung ermöglichte.

Manipulation versteckter Felder (E-Commerce, fortlaufend)

Mehrere E-Commerce-Plattformen wurden durch Änderung versteckter Formularfelder mit Preisen, Mengen oder Benutzer-IDs ausgenutzt, wodurch Angreifer Transaktionen manipulieren könnten.


Tools zum Testen und Ausnutzen

  • Burp Suite -- Web-Sicherheitstool zum Abfangen und Ändern von HTTP-Anfragen einschließlich Cookies und versteckter Felder.

  • OWASP ZAP -- Open-Source Web-Anwendungs-Sicherheitsscanner mit Anfrage-Abfangfunktionen.

  • Browser DevTools -- Integrierte Browser-Tools zum Ändern von Cookies, Formularen und Headern.


CVE-Beispiele

  • CVE-2002-1730 -- Authentifizierungsumgehung durch Setzen eines Cookies auf "true".

  • CVE-2002-1734 -- Authentifizierungsumgehung durch Cookie-Manipulation.

  • CVE-2002-2064 -- Admin-Zugang durch Setzen eines Cookie-Werts.

  • CVE-2005-1708 -- Umgehung durch Setzen einer Admin-Variable auf true.

  • CVE-2005-1787 -- Rechteeskalation durch Manipulation versteckter Felder.


Referenzen

  1. MITRE Corporation. "CWE-302: Authentication Bypass by Assumed-Immutable Data." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/302.html

  2. OWASP Foundation. "Broken Access Control." OWASP Top Ten. https://owasp.org/Top10/A01_2021-Broken_Access_Control/

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