Verwendung von Ein-Faktor-Authentifizierung
Beschreibung
Verwendung von Ein-Faktor-Authentifizierung ist eine Schwachstelle, die auftritt, wenn ein Produkt sich nur auf einen Authentifizierungsfaktor (typischerweise ein Passwort) verlässt, obwohl mehrere Authentifizierungsfaktoren für angemessene Sicherheit erforderlich wären. Authentifizierungsfaktoren werden kategorisiert als: Wissen (Passwörter, PINs), Besitz (Token, Smartcards, Telefone) und Biometrie (Fingerabdrücke, Gesichtserkennung). Während Ein-Faktor-Authentifizierung für Anwendungen mit niedrigem Risiko angemessen sein kann, erfordern sensible Systeme, die Finanzdaten, Gesundheitsinformationen, administrativen Zugriff oder persönliche Daten verarbeiten, mehrere unabhängige Faktoren für angemessenen Schutz gegen Credential-Diebstahl und unbefugten Zugriff.
Risiko
Ein-Faktor-Authentifizierung, insbesondere reine Passwortsysteme, stellt eine erhebliche Sicherheitsschwäche dar, da die Kompromittierung dieses einzelnen Faktors vollständigen Kontozugriff gewährt. Passwörter sind für zahlreiche Angriffsvektoren anfällig: Phishing, Credential Stuffing, Brute Force, Keylogger, Shoulder Surfing und Datenlecks, die Anmeldedatenbanken exponieren. Sobald ein Passwort kompromittiert ist, haben Angreifer uneingeschränkten Zugriff auf das Konto, unabhängig davon, wie stark andere Sicherheitsmaßnahmen sein mögen. Das Risiko wird durch weit verbreitete Passwortwiederverwendung verstärkt - ein Datenleck bei einem Dienst kompromittiert oft Konten bei mehreren Diensten. Hochwertige Ziele wie Administratorkonten, Finanzsysteme und Gesundheitsanwendungen sind besonders gefährdet, da Angreifer diese wegen ihres potenziellen Schadens gezielt angreifen.
Lösung
Implementieren Sie Multi-Faktor-Authentifizierung (MFA) für alle sensiblen Zugriffe und kritischen Funktionen. Fordern Sie mindestens zwei unabhängige Faktoren aus verschiedenen Kategorien - typischerweise Wissen kombiniert mit Besitz. Setzen Sie zeitbasierte Einmalpasswörter (TOTP), Hardware-Sicherheitsschlüssel (FIDO2/WebAuthn), Push-Benachrichtigungen oder SMS-Codes als zweite Faktoren ein, mit Präferenz für Phishing-resistente Methoden wie Hardware-Schlüssel. Wenden Sie risikobasierte Authentifizierung an, die Anforderungen basierend auf Login-Kontext eskaliert (neues Gerät, ungewöhnlicher Standort, sensible Operation). Stellen Sie sicher, dass MFA nicht über einfache Kontoeinstellungen ohne zusätzliche Verifikation deaktiviert werden kann. Erwägen Sie passwortlose Authentifizierung mit Hardware-Token oder Biometrie für Szenarien mit höchsten Sicherheitsanforderungen. Bieten Sie Fallback-Mechanismen, die Sicherheit aufrechterhalten und gleichzeitig Kontowiederherstellung ermöglichen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Wenn das einzelne Authentifizierungsgeheimnis kompromittiert wird, erlangen Angreifer vollen Zugriff auf das Konto. Keine zusätzlichen Barrieren verhindern unbefugten Zugriff, sobald das Passwort bekannt ist. |
| Integrität, Vertraulichkeit | Bereich: Integrität, Vertraulichkeit Vollständige Kontokompromittierung ermöglicht Datendiebstahl, unbefugte Transaktionen, Identitätsfälschung und potenzielle laterale Bewegung zu anderen Systemen unter Verwendung gesammelter Anmeldedaten. |
Beispielcode
Anfälliger Code (Java)
Die folgenden Beispiele demonstrieren Ein-Faktor-Authentifizierungs-Implementierungen:
// Anfällig: Nur-Passwort-Authentifizierung für sensibles System
import java.security.MessageDigest;
import javax.servlet.http.*;
public class VulnerableBankingAuth extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
// Anfällig: Nur Passwort-Authentifizierung für Banking-System
if (authenticateUser(username, password)) {
// Voller Zugriff nur mit einem Passwort gewährt!
HttpSession session = request.getSession();
session.setAttribute("authenticated", true);
session.setAttribute("username", username);
// Kann jetzt auf sensible Banking-Funktionen zugreifen
response.sendRedirect("/dashboard");
} else {
response.sendRedirect("/login?error=invalid");
}
}
// Anfällig: Schwacher Hash ohne Salt
private boolean authenticateUser(String username, String password) {
User user = userRepository.findByUsername(username);
if (user == null) return false;
// Ebenfalls anfällig: SHA-1 ist schwach
String hashedInput = sha1Hash(password);
return hashedInput.equals(user.getPasswordHash());
}
private String sha1Hash(String input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] hash = md.digest(input.getBytes());
return bytesToHex(hash);
} catch (Exception e) {
return null;
}
}
}
# Anfällig: Admin-Portal mit nur Passwort-Authentifizierung
from flask import Flask, request, session, redirect
app = Flask(__name__)
@app.route('/admin/login', methods=['POST'])
def admin_login():
username = request.form.get('username')
password = request.form.get('password')
# Anfällig: Admin-Zugriff nur mit Passwort
admin = Admin.query.filter_by(username=username).first()
if admin and admin.check_password(password):
# Voller Admin-Zugriff mit nur einem Faktor!
session['admin_authenticated'] = True
session['admin_id'] = admin.id
session['admin_role'] = admin.role # Könnte Superadmin sein!
return redirect('/admin/dashboard')
return redirect('/admin/login?error=1')
# Anfällig: Sensible Operationen ohne zweiten Faktor
@app.route('/admin/users/delete/<user_id>', methods=['POST'])
def delete_user(user_id):
# Prüft nur Session, keine zusätzliche Verifikation
if session.get('admin_authenticated'):
User.query.filter_by(id=user_id).delete()
db.session.commit()
return redirect('/admin/users')
return redirect('/admin/login')
// Anfällig: C-Authentifizierung nur mit Passwort
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
typedef struct {
char username[64];
char password_hash[65];
int privilege_level; // 0=Benutzer, 1=Admin, 2=Superadmin
} UserRecord;
// Anfällig: Gibt vollen Zugriff einschließlich Admin nur mit Passwort zurück
int vulnerable_authenticate(const char *username, const char *password) {
UserRecord *user = lookup_user(username);
if (user == NULL) {
return -1; // Benutzer nicht gefunden
}
char input_hash[65];
compute_sha256(password, input_hash);
// Anfällig: Passwort allein gewährt jede Berechtigungsstufe
if (strcmp(input_hash, user->password_hash) == 0) {
return user->privilege_level; // Gibt 2 für Superadmin zurück!
}
return -1; // Authentifizierung fehlgeschlagen
}
int main() {
char username[64], password[64];
printf("Benutzername: ");
scanf("%63s", username);
printf("Passwort: ");
scanf("%63s", password);
int level = vulnerable_authenticate(username, password);
if (level >= 0) {
// Anfällig: Voller Zugriff mit nur einem Faktor
if (level == 2) {
printf("Willkommen, Superadmin!\n");
superadmin_menu(); // Volle Systemkontrolle!
}
}
return 0;
}
Korrigierter Code (Java)
// Korrigiert: Multi-Faktor-Authentifizierung für sensibles System
import java.security.SecureRandom;
import javax.servlet.http.*;
public class SecureBankingAuth extends HttpServlet {
private final TOTPService totpService;
private final MFAEnrollmentService mfaService;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
String action = request.getParameter("action");
if ("password".equals(action)) {
handlePasswordStep(request, response);
} else if ("mfa".equals(action)) {
handleMFAStep(request, response);
}
}
// Korrigiert: Passwort ist nur der erste Faktor
private void handlePasswordStep(HttpServletRequest request,
HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (authenticatePassword(username, password)) {
// Korrigiert: Passwort verifiziert, aber noch nicht vollständig authentifiziert
HttpSession session = request.getSession();
session.setAttribute("pending_mfa", true);
session.setAttribute("pending_username", username);
session.setMaxInactiveInterval(300); // 5 Min um MFA abzuschließen
// Korrigiert: Weiterleitung zum MFA-Schritt
response.sendRedirect("/login/mfa");
} else {
response.sendRedirect("/login?error=invalid");
}
}
// Korrigiert: Zweiter Faktor erforderlich
private void handleMFAStep(HttpServletRequest request,
HttpServletResponse response) {
HttpSession session = request.getSession(false);
// Verifizieren dass erster Faktor abgeschlossen wurde
if (session == null || !Boolean.TRUE.equals(session.getAttribute("pending_mfa"))) {
response.sendRedirect("/login");
return;
}
String username = (String) session.getAttribute("pending_username");
String totpCode = request.getParameter("totp_code");
// Korrigiert: TOTP zweiten Faktor verifizieren
if (totpService.verifyCode(username, totpCode)) {
// Ausstehenden Zustand löschen
session.removeAttribute("pending_mfa");
session.removeAttribute("pending_username");
// Korrigiert: Erst jetzt ist Benutzer vollständig authentifiziert
session.setAttribute("authenticated", true);
session.setAttribute("username", username);
session.setAttribute("auth_time", System.currentTimeMillis());
auditService.logSuccessfulMFA(username);
response.sendRedirect("/dashboard");
} else {
auditService.logFailedMFA(username);
// Nicht verraten welcher Faktor fehlgeschlagen ist
session.invalidate();
response.sendRedirect("/login?error=invalid");
}
}
// Korrigiert: Starkes Passwort-Hashing mit Salt
private boolean authenticatePassword(String username, String password) {
User user = userRepository.findByUsername(username);
// Zeitkonstante Prüfung um Enumeration zu verhindern
if (user == null) {
passwordEncoder.encode(password); // Dummy-Operation
return false;
}
// Verwendung von bcrypt/argon2 für Passwortverifikation
return passwordEncoder.matches(password, user.getPasswordHash());
}
}
// Korrigiert: TOTP-Verifikationsdienst
@Service
public class TOTPService {
private static final int TIME_STEP_SECONDS = 30;
private static final int CODE_DIGITS = 6;
public boolean verifyCode(String username, String code) {
User user = userRepository.findByUsername(username);
if (user == null || user.getTotpSecret() == null) {
return false;
}
// Uhrendrift erlauben (1 Schritt vor und nach)
long currentTime = System.currentTimeMillis() / 1000;
for (int i = -1; i <= 1; i++) {
String expectedCode = generateTOTP(
user.getTotpSecret(),
(currentTime / TIME_STEP_SECONDS) + i
);
if (MessageDigest.isEqual(code.getBytes(), expectedCode.getBytes())) {
// Code-Wiederverwendung verhindern
if (isCodeAlreadyUsed(username, code)) {
return false;
}
markCodeUsed(username, code);
return true;
}
}
return false;
}
}
# Korrigiert: Multi-Faktor-Authentifizierung für Admin-Portal
from flask import Flask, request, session, redirect
import pyotp
from functools import wraps
app = Flask(__name__)
def require_mfa(f):
"""Decorator der abgeschlossene MFA erfordert."""
@wraps(f)
def decorated(*args, **kwargs):
if not session.get('mfa_verified'):
return redirect('/admin/login')
return f(*args, **kwargs)
return decorated
def require_step_up(f):
"""Decorator der Step-Up-Authentifizierung für sensible Ops erfordert."""
@wraps(f)
def decorated(*args, **kwargs):
# Prüfen ob Step-Up-Auth kürzlich abgeschlossen wurde
step_up_time = session.get('step_up_time', 0)
if time.time() - step_up_time > 300: # 5-Minuten-Fenster
session['pending_action'] = request.url
return redirect('/admin/step-up')
return f(*args, **kwargs)
return decorated
@app.route('/admin/login', methods=['POST'])
def admin_login():
step = request.form.get('step', 'password')
if step == 'password':
username = request.form.get('username')
password = request.form.get('password')
admin = Admin.query.filter_by(username=username).first()
if admin and admin.check_password(password):
# Korrigiert: Passwort verifiziert, MFA erforderlich
session['pending_admin'] = admin.id
session['pending_username'] = username
return redirect('/admin/mfa')
return redirect('/admin/login?error=1')
elif step == 'mfa':
if 'pending_admin' not in session:
return redirect('/admin/login')
totp_code = request.form.get('totp_code')
admin = Admin.query.get(session['pending_admin'])
if admin and verify_totp(admin.totp_secret, totp_code):
# Korrigiert: Beide Faktoren verifiziert
session.pop('pending_admin')
session.pop('pending_username')
session['admin_authenticated'] = True
session['admin_id'] = admin.id
session['mfa_verified'] = True
session['auth_time'] = time.time()
return redirect('/admin/dashboard')
return redirect('/admin/mfa?error=1')
# Korrigiert: Sensible Operationen erfordern Step-Up-Authentifizierung
@app.route('/admin/users/delete/<user_id>', methods=['POST'])
@require_mfa
@require_step_up # Erfordert Re-Authentifizierung
def delete_user(user_id):
# Zusätzliche Verifikation für destruktive Aktion
User.query.filter_by(id=user_id).delete()
db.session.commit()
audit_log.record('user_deleted', user_id, session['admin_id'])
return redirect('/admin/users')
def verify_totp(secret, code):
"""TOTP-Code mit Uhrendrift-Toleranz verifizieren."""
totp = pyotp.TOTP(secret)
return totp.verify(code, valid_window=1)
Die Korrektur implementiert Multi-Faktor-Authentifizierung, die sowohl Passwort- als auch TOTP-Verifikation erfordert.
Ausgenutzt in der Praxis
MFA-Umgehung in Chat-Anwendung (2022)
CVE-2022-35248 dokumentierte eine Chat-Anwendung, bei der der zweite Faktor der Zwei-Faktor-Authentifizierung übersprungen wurde, wenn Central Authentication Service (CAS) aktiviert war, was die Sicherheit effektiv auf Ein-Faktor reduzierte.
Credential-Stuffing-Angriffe (Fortlaufend)
Zahlreiche hochkarätige Datenlecks haben Ein-Faktor-Authentifizierung ausgenutzt, indem Anmeldedaten aus anderen Datenlecks verwendet wurden, um unbefugten Zugriff auf nur durch Passwörter geschützte Konten zu erlangen.
Tools zum Testen/Ausnutzen
-
Credential Stuffing Tools — Tools die auf Ein-Faktor-Schwachstellen mit geleakten Anmeldedaten testen.
-
Burp Suite — Web-Sicherheitstool zum Testen von Authentifizierungsmechanismen.
-
MFA Configuration Scanners — Tools zur Bewertung von MFA-Implementierung und -Abdeckung.
CVE-Beispiele
-
CVE-2022-35248 — MFA zweiter Faktor übersprungen wenn CAS aktiviert.
-
CVE-2020-12812 — MFA-Umgehung durch API-Endpunkt.
-
CVE-2018-0114 — JWT-Authentifizierungsumgehung in Webanwendung.
Referenzen
-
MITRE Corporation. "CWE-308: Use of Single-factor Authentication." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/308.html
-
NIST. "Digital Identity Guidelines: Authentication and Lifecycle Management." SP 800-63B. https://pages.nist.gov/800-63-3/sp800-63b.html
-
OWASP Foundation. "Multifactor Authentication Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Multifactor_Authentication_Cheat_Sheet.html