Vertrauen auf einen einzelnen Faktor bei einer Sicherheitsentscheidung

Beschreibung

Das Vertrauen auf einen einzelnen Faktor bei einer Sicherheitsentscheidung tritt auf, wenn eine Anwendung nur eine einzelne Information verwendet, um sicherheitskritische Entscheidungen zu treffen. Dies umfasst die Verwendung nur eines Passworts ohne MFA, Vertrauen nur auf IP-Adresse für Zugriffskontrolle, ausschließliches Verlassen auf vom Client bereitgestellte Daten oder die Verwendung einer einzelnen Prüfung für Autorisierung. Wenn dieser einzelne Faktor kompromittiert wird, versagt die gesamte Sicherheitskontrolle.

Risiko

Diebstahl von Anmeldedaten führt zu vollständiger Kontokompromittierung. IP-Spoofing umgeht netzwerkbasierte Zugriffskontrollen. Einzelne Fehlerpunkte in der Sicherheitsarchitektur. Social-Engineering-Angriffe sind mit einer Information erfolgreich. Replay-Angriffe sind erfolgreich wenn Token nicht mit anderen Faktoren kombiniert werden. Automatisierte Angriffe sind einfacher ohne mehrere Herausforderungen.

Lösung

Implementieren Sie Multi-Faktor-Authentifizierung. Verwenden Sie Defense-in-Depth mit mehreren Sicherheitsebenen. Kombinieren Sie etwas das Sie wissen, haben und sind. Verifizieren Sie mehrere unabhängige Attribute. Implementieren Sie risikobasierte Authentifizierung. Verwenden Sie Verhaltensanalyse neben Anmeldedaten. Verlassen Sie sich nicht allein auf einen einzelnen Identifikator.

Häufige Konsequenzen

AuswirkungDetails
AuthentifizierungUmfang: Kontoübernahme

Kompromittierung eines einzelnen Faktors gewährt vollen Zugriff.
AutorisierungUmfang: Zugriffskontrollumgehung

Einzelne Prüfung leicht umgangen.
Nicht-AbstreitbarkeitUmfang: Identitätsvortäuschung

Benutzeridentität kann mit einzelnem Faktor nicht bewiesen werden.

Beispielcode + Lösungscode

Anfälliger Code

// ANFÄLLIG: Nur Passwort-Authentifizierung
public class VulnerableSingleFactorAuth {

    public AuthResult authenticate(String username, String password) {
        User user = userRepository.findByUsername(username);

        // ANFÄLLIG: Nur Passwortprüfung
        if (user != null && passwordEncoder.matches(password, user.getPasswordHash())) {
            return AuthResult.success(user);
        }

        return AuthResult.failure();
    }
}

// ANFÄLLIG: Nur IP-basierte Zugriffskontrolle
public class VulnerableIPBasedAuth {

    private Set<String> allowedIPs = Set.of("10.0.0.1", "10.0.0.2");

    public boolean isAuthorized(HttpServletRequest request) {
        // ANFÄLLIG: IP-Adresse kann gespooft werden
        String clientIP = request.getRemoteAddr();
        return allowedIPs.contains(clientIP);
    }
}

// ANFÄLLIG: Einzelnes Token für allen Zugriff
public class VulnerableSingleTokenAuth {

    public boolean validateAccess(String token) {
        // ANFÄLLIG: Token allein gewährt allen Zugriff
        // Wenn Token gestohlen wird, hat Angreifer vollen Zugriff
        return tokenService.isValid(token);
    }

    public void performSensitiveAction(String token) {
        // ANFÄLLIG: Keine zusätzliche Verifizierung für sensible Aktionen
        if (validateAccess(token)) {
            executeSensitiveOperation();
        }
    }
}

// ANFÄLLIG: Nur Referer-CSRF-Schutz
public class VulnerableRefererCheck {

    public boolean isValidRequest(HttpServletRequest request) {
        // ANFÄLLIG: Referer kann manipuliert/entfernt werden
        String referer = request.getHeader("Referer");
        return referer != null && referer.startsWith("https://myapp.com");
    }
}
# ANFÄLLIG: Einzelfaktor-Authentifizierung
class VulnerableAuth:
    def authenticate(self, username, password):
        user = get_user(username)

        # ANFÄLLIG: Nur Passwort
        if user and check_password(password, user.password_hash):
            return create_session(user)

        return None

    # ANFÄLLIG: Nur API-Schlüssel
    def authenticate_api(self, api_key):
        # Einzelner Schlüssel gewährt vollen API-Zugriff
        if api_key in valid_api_keys:
            return True
        return False

# ANFÄLLIG: Nur Cookie-Sitzungsvalidierung
class VulnerableSessionAuth:
    def validate_session(self, session_id):
        # ANFÄLLIG: Prüft nur Sitzungs-Cookie
        # Wenn Cookie gestohlen wird (XSS), hat Angreifer vollen Zugriff
        session = get_session(session_id)
        return session is not None

    def transfer_funds(self, session_id, amount, to_account):
        # ANFÄLLIG: Keine erneute Authentifizierung für sensible Aktion
        if self.validate_session(session_id):
            execute_transfer(amount, to_account)

# ANFÄLLIG: Nur Sicherheitsfrage für Passwort-Reset
class VulnerablePasswordReset:
    def verify_identity(self, username, answer):
        user = get_user(username)
        # ANFÄLLIG: Einzelne Frage, oft erratbar
        if user.security_answer.lower() == answer.lower():
            return True
        return False

    def reset_password(self, username, answer, new_password):
        # ANFÄLLIG: Einzelner Faktor für kritische Aktion
        if self.verify_identity(username, answer):
            set_password(username, new_password)
// ANFÄLLIG: Einzelfaktor-Auth in Node.js
class VulnerableAuth {
    async authenticate(username, password) {
        const user = await db.findUser(username);

        // ANFÄLLIG: Nur Passwort
        if (user && await bcrypt.compare(password, user.passwordHash)) {
            return this.createToken(user);
        }

        return null;
    }

    // ANFÄLLIG: Nur Bearer-Token
    validateRequest(req) {
        const token = req.headers.authorization?.split(' ')[1];
        // ANFÄLLIG: Token allein gewährt Zugriff
        // Kein Device-Fingerprinting, keine Standortprüfung
        return this.verifyToken(token);
    }
}

// ANFÄLLIG: Nur Origin-CORS-Schutz
app.use((req, res, next) => {
    const origin = req.headers.origin;
    // ANFÄLLIG: Origin kann in einigen Szenarien gespooft werden
    if (allowedOrigins.includes(origin)) {
        res.setHeader('Access-Control-Allow-Origin', origin);
    }
    next();
});

// ANFÄLLIG: User-Agent für Bot-Erkennung
function isBot(req) {
    // ANFÄLLIG: User-Agent leicht zu spoofen
    const ua = req.headers['user-agent'];
    return botPatterns.some(pattern => pattern.test(ua));
}

// ANFÄLLIG: Nur E-Mail-Verifizierung für Kontowiederherstellung
async function recoverAccount(email) {
    const user = await db.findByEmail(email);
    if (user) {
        // ANFÄLLIG: E-Mail-Zugang ist einzelner Faktor
        // Wenn E-Mail kompromittiert, ist Konto kompromittiert
        const token = generateResetToken();
        await sendResetEmail(email, token);
    }
}
<?php
// ANFÄLLIG: Nur Passwort-Authentifizierung
class VulnerableAuth {
    public function login($username, $password) {
        $user = $this->getUser($username);

        // ANFÄLLIG: Einzelner Faktor
        if ($user && password_verify($password, $user['password_hash'])) {
            $_SESSION['user_id'] = $user['id'];
            return true;
        }
        return false;
    }

    // ANFÄLLIG: Nur Sitzung für Admin-Aktionen
    public function deleteUser($targetUserId) {
        // ANFÄLLIG: Keine erneute Authentifizierung für destruktive Aktion
        if (isset($_SESSION['user_id'])) {
            $this->db->delete('users', $targetUserId);
            return true;
        }
        return false;
    }
}

// ANFÄLLIG: Nur API-Schlüssel
function authenticateAPI() {
    $apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
    // ANFÄLLIG: Schlüssel allein gewährt vollen Zugriff
    return in_array($apiKey, getValidApiKeys());
}

// ANFÄLLIG: Nur CAPTCHA für Registrierung
function registerUser($data, $captchaResponse) {
    // ANFÄLLIG: CAPTCHA ist nur Bot-Prävention
    // Keine E-Mail-Verifizierung, keine Telefon-Verifizierung
    if (verifyCaptcha($captchaResponse)) {
        createUser($data);
        return true;
    }
    return false;
}
?>

Korrigierter Code

// SICHER: Multi-Faktor-Authentifizierung
public class MultiFactorAuth {

    private final PasswordEncoder passwordEncoder;
    private final TotpService totpService;
    private final RiskAnalysisService riskService;

    public AuthResult authenticate(AuthRequest request) {
        User user = userRepository.findByUsername(request.getUsername());
        if (user == null) {
            return AuthResult.failure("Ungültige Anmeldedaten");
        }

        // Faktor 1: Passwort (etwas das Sie wissen)
        if (!passwordEncoder.matches(request.getPassword(), user.getPasswordHash())) {
            return AuthResult.failure("Ungültige Anmeldedaten");
        }

        // Faktor 2: TOTP (etwas das Sie haben)
        if (user.hasMfaEnabled()) {
            if (!totpService.verify(user.getTotpSecret(), request.getTotpCode())) {
                return AuthResult.failure("Ungültiger MFA-Code");
            }
        }

        // Faktor 3: Risikobasierte Prüfungen (verhaltensbasiert)
        RiskAssessment risk = riskService.assess(request, user);
        if (risk.isHigh()) {
            // Erfordere zusätzliche Verifizierung
            return AuthResult.requireAdditionalVerification(risk.getReason());
        }

        return AuthResult.success(user);
    }

    // SICHER: Erneute Authentifizierung für sensible Aktionen
    public void performSensitiveAction(User user, String password, String totpCode) {
        // Verifiziere Anmeldedaten erneut vor sensibler Aktion
        if (!passwordEncoder.matches(password, user.getPasswordHash())) {
            throw new AuthenticationException("Ungültiges Passwort");
        }

        if (user.hasMfaEnabled()) {
            if (!totpService.verify(user.getTotpSecret(), totpCode)) {
                throw new AuthenticationException("Ungültiger MFA-Code");
            }
        }

        executeSensitiveOperation();
    }
}

// SICHER: Mehrere Faktoren für Zugriffskontrolle
public class MultiFactorAccessControl {

    public boolean isAuthorized(HttpServletRequest request, User user) {
        // Faktor 1: Gültige Sitzung
        if (!sessionService.isValidSession(request)) {
            return false;
        }

        // Faktor 2: IP im erlaubten Bereich (für Admin)
        if (user.isAdmin()) {
            String ip = getClientIP(request);
            if (!allowedAdminIPs.contains(ip)) {
                return false;
            }
        }

        // Faktor 3: Geräte-Fingerprint stimmt mit bekanntem Gerät überein
        String fingerprint = request.getHeader("X-Device-Fingerprint");
        if (!user.getKnownDevices().contains(fingerprint)) {
            // Markiere für zusätzliche Verifizierung
            notifySecurityTeam(user, "Unbekannter Gerätezugriff");
        }

        // Faktor 4: Kein anomales Verhalten
        if (riskService.detectAnomaly(user, request)) {
            return false;
        }

        return true;
    }
}
# SICHER: Multi-Faktor-Authentifizierung in Python
import pyotp
from flask import request

class MultiFactorAuth:
    def __init__(self, risk_service, device_service):
        self.risk_service = risk_service
        self.device_service = device_service

    def authenticate(self, username, password, totp_code=None, device_id=None):
        user = get_user(username)
        if not user:
            return None

        # Faktor 1: Passwort
        if not check_password(password, user.password_hash):
            self.log_failed_attempt(username, 'password')
            return None

        # Faktor 2: TOTP (wenn aktiviert)
        if user.mfa_enabled:
            if not totp_code:
                return {'status': 'mfa_required'}

            totp = pyotp.TOTP(user.totp_secret)
            if not totp.verify(totp_code):
                self.log_failed_attempt(username, 'totp')
                return None

        # Faktor 3: Geräteverifizierung
        if device_id:
            if not self.device_service.is_known_device(user.id, device_id):
                # Neues Gerät - sende Verifizierungs-E-Mail
                self.send_device_verification(user, device_id)
                return {'status': 'device_verification_required'}

        # Faktor 4: Risikobewertung
        risk = self.risk_service.assess(user, request)
        if risk.score > 0.8:
            return {'status': 'additional_verification_required', 'reason': risk.factors}

        return {'status': 'success', 'user': user}

    def transfer_funds(self, session, amount, to_account, password, totp_code):
        user = session.user

        # Erneute Authentifizierung für sensible Aktion
        if not check_password(password, user.password_hash):
            raise AuthenticationError("Ungültiges Passwort")

        if user.mfa_enabled:
            totp = pyotp.TOTP(user.totp_secret)
            if not totp.verify(totp_code):
                raise AuthenticationError("Ungültiger MFA-Code")

        # Zusätzliche Prüfung: Transaktionslimits
        if amount > user.transaction_limit:
            raise SecurityError("Überschreitet Transaktionslimit")

        execute_transfer(user, amount, to_account)

# SICHER: Mehrschichtige API-Authentifizierung
class SecureAPIAuth:
    def authenticate_request(self, request):
        # Faktor 1: API-Schlüssel
        api_key = request.headers.get('X-API-Key')
        if not api_key or api_key not in valid_api_keys:
            return None

        # Faktor 2: Request-Signatur (HMAC)
        signature = request.headers.get('X-Signature')
        expected_sig = self.compute_signature(request, api_key)
        if not hmac.compare_digest(signature, expected_sig):
            return None

        # Faktor 3: Zeitstempel (verhindere Replay)
        timestamp = request.headers.get('X-Timestamp')
        if not self.is_recent_timestamp(timestamp):
            return None

        # Faktor 4: IP-Whitelist (wenn konfiguriert)
        api_config = get_api_config(api_key)
        if api_config.ip_whitelist:
            if request.remote_addr not in api_config.ip_whitelist:
                return None

        return api_config.client_id
// SICHER: Multi-Faktor-Authentifizierung in Node.js
const speakeasy = require('speakeasy');

class MultiFactorAuth {
    async authenticate(credentials) {
        const { username, password, totpCode, deviceId } = credentials;

        const user = await db.findUser(username);
        if (!user) {
            return { success: false };
        }

        // Faktor 1: Passwort
        if (!await bcrypt.compare(password, user.passwordHash)) {
            await this.logFailedAttempt(username, 'password');
            return { success: false };
        }

        // Faktor 2: TOTP
        if (user.mfaEnabled) {
            if (!totpCode) {
                return { success: false, mfaRequired: true };
            }

            const verified = speakeasy.totp.verify({
                secret: user.totpSecret,
                encoding: 'base32',
                token: totpCode
            });

            if (!verified) {
                await this.logFailedAttempt(username, 'totp');
                return { success: false };
            }
        }

        // Faktor 3: Geräteverifizierung
        if (deviceId) {
            const knownDevice = await db.isKnownDevice(user.id, deviceId);
            if (!knownDevice) {
                await this.sendDeviceVerificationEmail(user, deviceId);
                return { success: false, deviceVerificationRequired: true };
            }
        }

        // Faktor 4: Risikobasierte Analyse
        const risk = await this.riskService.analyze(user, credentials);
        if (risk.score > 0.7) {
            return {
                success: false,
                additionalVerificationRequired: true,
                riskFactors: risk.factors
            };
        }

        return { success: true, user };
    }

    // SICHER: Sensible Aktion erfordert erneute Authentifizierung
    async transferFunds(session, transferData) {
        const { amount, toAccount, password, totpCode } = transferData;
        const user = session.user;

        // Verifiziere Passwort erneut
        if (!await bcrypt.compare(password, user.passwordHash)) {
            throw new AuthError('Ungültiges Passwort');
        }

        // Verifiziere MFA erneut
        if (user.mfaEnabled) {
            const verified = speakeasy.totp.verify({
                secret: user.totpSecret,
                encoding: 'base32',
                token: totpCode
            });

            if (!verified) {
                throw new AuthError('Ungültiger MFA-Code');
            }
        }

        // Fahre mit Überweisung fort
        await this.executeTransfer(user, amount, toAccount);
    }
}

Ausgenutzt in der Praxis

Credential Stuffing

Nur-Passwort-Auth anfällig für gestopfte Anmeldedaten.

SIM-Swapping

Nur-SMS 2FA umgangen über Carrier Social Engineering.

Session Hijacking

Nur-Token-Auth kompromittiert über XSS.


Tools zum Testen/Ausnutzen

  • Credential-Testing-Tools (Hydra, Medusa).

  • Session-Hijacking-Tools.

  • MFA-Bypass-Techniken.


CVE-Beispiele

  • Kontoübernahmen durch Einzelfaktor-Systeme.

  • API-Breaches über exponierte API-Schlüssel.


Referenzen

  1. MITRE. "CWE-654: Reliance on a Single Factor in a Security Decision." https://cwe.mitre.org/data/definitions/654.html

  2. NIST SP 800-63B. "Digital Identity Guidelines."