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
| Auswirkung | Details |
|---|---|
| Authentifizierung | Umfang: Kontoübernahme Kompromittierung eines einzelnen Faktors gewährt vollen Zugriff. |
| Autorisierung | Umfang: Zugriffskontrollumgehung Einzelne Prüfung leicht umgangen. |
| Nicht-Abstreitbarkeit | Umfang: 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
-
MITRE. "CWE-654: Reliance on a Single Factor in a Security Decision." https://cwe.mitre.org/data/definitions/654.html
-
NIST SP 800-63B. "Digital Identity Guidelines."