Einfügen sensibler Informationen in Logdateien

Beschreibung

Einfügen sensibler Informationen in Logdateien tritt auf, wenn Software sensible Informationen in eine Logdatei schreibt, die für Angreifer oder unbefugte Parteien zugänglich ist. Logdateien haben oft schwächere Zugriffskontrollen als Anwendungsdaten und können an zentralisierte Logging-Systeme übertragen, in Backups gespeichert oder von Support-Personal eingesehen werden. Sensible Daten in Logs können Passwörter, API-Schlüssel, Kreditkartennummern, persönliche Informationen, Sitzungstokens und kryptografische Geheimnisse umfassen.

Risiko

Sensible Informationen in Logs erzeugen mehrere Angriffsvektoren. Logs können für Entwickler, Betreiber oder Drittanbieter-Services zugänglich sein, die Produktionsdaten nicht sehen sollten. Log-Aggregations-Services können Daten in weniger sicheren Umgebungen speichern. Logs werden oft in Backups eingeschlossen und können für längere Zeiträume aufbewahrt werden. Angreifer, die Lesezugriff auf Logs erlangen (durch Directory Traversal, SSRF oder Log-Injection), erhalten sensible Daten direkt. Compliance-Frameworks verbieten das Logging bestimmter Datentypen.

Lösung

Loggen Sie niemals Passwörter, API-Schlüssel, Kreditkartennummern oder Authentifizierungstokens. Maskieren oder kürzen Sie sensible Daten vor dem Logging. Implementieren Sie Logging-Levels, die sensible Debug-Informationen in Produktion ausschließen. Verwenden Sie strukturiertes Logging mit expliziten Feldern zur Kontrolle, was geloggt wird. Überprüfen Sie Logs regelmäßig auf Offenlegung sensibler Daten. Konfigurieren Sie Log-Frameworks, um bekannte sensible Muster zu schwärzen. Verschlüsseln Sie Logs im Ruhezustand und während der Übertragung. Wenden Sie Zugriffskontrollen auf Logdateien an.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Anmeldedaten-Offenlegung

Geloggte Passwörter, Tokens und API-Schlüssel können für unbefugten Zugriff verwendet werden.
DatenschutzBereich: PII-Offenlegung

Persönliche Informationen in Logs verletzen Datenschutzvorschriften (DSGVO, CCPA).
ComplianceBereich: Regulatorische Verstöße

Das Logging von Kreditkartendaten verstößt gegen PCI-DSS; das Logging von Gesundheitsdaten kann HIPAA verletzen.

Beispielcode + Lösungscode

Verwundbarer Code

# VERWUNDBAR: Passwörter loggen
import logging
logger = logging.getLogger(__name__)

def authenticate(username, password):
    logger.info(f"Login-Versuch: username={username}, password={password}")
    # Passwort im Klartext geloggt!

    user = User.query.filter_by(username=username).first()
    if user and user.check_password(password):
        logger.info(f"Erfolgreicher Login für {username} mit Passwort {password}")
        return user
    return None

# VERWUNDBAR: API-Schlüssel loggen
def call_external_api(api_key, endpoint):
    logger.debug(f"Rufe {endpoint} mit API-Schlüssel auf: {api_key}")
    response = requests.get(endpoint, headers={'Authorization': api_key})
    return response

# VERWUNDBAR: Kreditkartendaten loggen
def process_payment(card_number, cvv, amount):
    logger.info(f"Verarbeite Zahlung: Karte={card_number}, CVV={cvv}, Betrag={amount}")
    # Vollständige Kartennummer und CVV geloggt!

# VERWUNDBAR: Vollständige Request/Response loggen
def proxy_request(request):
    logger.debug(f"Request-Header: {dict(request.headers)}")  # Kann Auth-Tokens enthalten
    logger.debug(f"Request-Body: {request.get_data()}")  # Kann Passwörter enthalten
// VERWUNDBAR: Benutzer-Anmeldedaten loggen
public class AuthService {

    private static final Logger logger = LoggerFactory.getLogger(AuthService.class);

    public User authenticate(String username, String password) {
        // KEINE PASSWÖRTER LOGGEN!
        logger.info("Authentifiziere Benutzer {} mit Passwort {}", username, password);

        User user = userRepository.findByUsername(username);
        if (user != null && passwordEncoder.matches(password, user.getPasswordHash())) {
            logger.info("Benutzer {} erfolgreich angemeldet mit Passwort {}", username, password);
            return user;
        }
        return null;
    }
}

// VERWUNDBAR: Tokens loggen
public class TokenService {

    public String generateToken(User user) {
        String token = jwtService.generate(user);
        logger.debug("Token für Benutzer {} generiert: {}", user.getUsername(), token);
        // Token kann verwendet werden, um Benutzer zu imitieren!
        return token;
    }
}

// VERWUNDBAR: PII loggen
public class UserService {

    public void updateProfile(UserProfileRequest request) {
        logger.info("Aktualisiere Profil: {}", request);  // Kann SSN, Geburtsdatum etc. loggen
    }
}
// VERWUNDBAR: Sensible Request-Daten loggen
app.post('/register', (req, res) => {
    console.log('Registrierungs-Request:', JSON.stringify(req.body));
    // Loggt Passwörter, persönliche Informationen!

    // Registrierung verarbeiten...
});

// VERWUNDBAR: Sitzungstokens loggen
app.post('/login', (req, res) => {
    const token = generateToken(req.user);
    console.log(`Sitzungstoken für ${req.user.email} generiert: ${token}`);
    // Token geloggt - kann aus Logs gestohlen werden

    res.json({ token });
});

// VERWUNDBAR: Allgemeines Fehler-Logging
app.use((err, req, res, next) => {
    console.error('Fehler:', err);
    console.error('Request:', req.body);  // Kann sensible Daten enthalten
    res.status(500).send('Fehler');
});

Lösungscode

# SICHER: Niemals sensible Daten loggen
import logging
import re

logger = logging.getLogger(__name__)

# Muster zum Schwärzen
SENSITIVE_PATTERNS = [
    (r'password["\s:=]+["\']?[\w]+["\']?', 'password=***GESCHWÄRZT***'),
    (r'\b\d{13,16}\b', '***KARTE_GESCHWÄRZT***'),  # Kreditkarte
    (r'\b\d{3}-\d{2}-\d{4}\b', '***SSN_GESCHWÄRZT***'),  # SSN
]

def sanitize_log_message(message):
    """Sensible Muster aus Log-Nachrichten schwärzen."""
    for pattern, replacement in SENSITIVE_PATTERNS:
        message = re.sub(pattern, replacement, message, flags=re.IGNORECASE)
    return message

class SanitizedFormatter(logging.Formatter):
    def format(self, record):
        record.msg = sanitize_log_message(str(record.msg))
        return super().format(record)

def authenticate(username, password):
    # Nur nicht-sensible Infos loggen
    logger.info(f"Login-Versuch für Benutzer: {username}")
    # NIEMALS Passwort loggen!

    user = User.query.filter_by(username=username).first()
    if user and user.check_password(password):
        logger.info(f"Erfolgreicher Login für Benutzer: {username}")
        return user

    logger.warning(f"Fehlgeschlagener Login-Versuch für Benutzer: {username}")
    return None

# SICHER: Sensible Daten maskieren
def mask_card_number(card_number):
    """Nur letzte 4 Ziffern anzeigen."""
    return f"***{card_number[-4:]}"

def mask_api_key(api_key):
    """Nur erste 4 Zeichen anzeigen."""
    return f"{api_key[:4]}***"

def call_external_api(api_key, endpoint):
    logger.debug(f"Rufe {endpoint} mit API-Schlüssel auf: {mask_api_key(api_key)}")
    response = requests.get(endpoint, headers={'Authorization': api_key})
    return response

def process_payment(card_number, cvv, amount):
    # Nur maskierte Daten loggen
    logger.info(f"Verarbeite Zahlung: Karte={mask_card_number(card_number)}, Betrag={amount}")
    # Niemals CVV loggen!

# SICHER: Strukturiertes Logging mit expliziten Feldern
import structlog

log = structlog.get_logger()

def process_order(order):
    log.info(
        "verarbeite_bestellung",
        order_id=order.id,
        amount=order.total,
        # Keine sensiblen Kundendaten einschließen
        customer_id=order.customer_id
    )
// SICHER: Niemals Anmeldedaten loggen
public class SecureAuthService {

    private static final Logger logger = LoggerFactory.getLogger(SecureAuthService.class);

    public User authenticate(String username, String password) {
        // Nur Benutzername loggen, NIEMALS Passwort
        logger.info("Authentifizierungsversuch für Benutzer: {}", username);

        User user = userRepository.findByUsername(username);
        if (user != null && passwordEncoder.matches(password, user.getPasswordHash())) {
            logger.info("Erfolgreiche Authentifizierung für Benutzer: {}", username);
            return user;
        }

        logger.warn("Fehlgeschlagener Authentifizierungsversuch für Benutzer: {}", username);
        return null;
    }
}

// SICHER: Tokens in Logs maskieren
public class SecureTokenService {

    public String generateToken(User user) {
        String token = jwtService.generate(user);
        // Nur loggen, dass Token generiert wurde, nicht das Token selbst
        logger.info("Token für Benutzer generiert: {}", user.getUsername());
        // Oder maskieren
        logger.debug("Token generiert: {}...", token.substring(0, 10));
        return token;
    }
}

// SICHER: Benutzerdefiniertes toString das sensible Felder ausschließt
public class UserProfileRequest {
    private String name;
    private String ssn;
    private String dateOfBirth;

    @Override
    public String toString() {
        return String.format("UserProfileRequest[name=%s, ssn=***GESCHWÄRZT***, dob=***GESCHWÄRZT***]", name);
    }
}

// SICHER: Annotationsbasierter Feldausschluss
@ToString(exclude = {"password", "ssn", "creditCard"})
public class User {
    private String username;
    private String password;
    private String ssn;
    private String creditCard;
}

// SICHER: Benutzerdefinierter Log-Bereiniger
public class LogSanitizer {

    private static final Pattern[] SENSITIVE_PATTERNS = {
        Pattern.compile("password[\"\\s:=]+[\"']?[\\w]+[\"']?", Pattern.CASE_INSENSITIVE),
        Pattern.compile("\\b\\d{13,16}\\b"),  // Kreditkarte
        Pattern.compile("bearer\\s+[A-Za-z0-9\\-._~+/]+=*", Pattern.CASE_INSENSITIVE)
    };

    public static String sanitize(String message) {
        String result = message;
        for (Pattern pattern : SENSITIVE_PATTERNS) {
            result = pattern.matcher(result).replaceAll("***GESCHWÄRZT***");
        }
        return result;
    }
}
// SICHER: Bereinigtes Logging
const sensitiveFields = ['password', 'token', 'apiKey', 'creditCard', 'ssn', 'cvv'];

function sanitizeObject(obj) {
    if (!obj || typeof obj !== 'object') return obj;

    const sanitized = Array.isArray(obj) ? [] : {};

    for (const [key, value] of Object.entries(obj)) {
        if (sensitiveFields.some(field =>
            key.toLowerCase().includes(field.toLowerCase()))) {
            sanitized[key] = '***GESCHWÄRZT***';
        } else if (typeof value === 'object') {
            sanitized[key] = sanitizeObject(value);
        } else {
            sanitized[key] = value;
        }
    }

    return sanitized;
}

const logger = {
    info: (msg, data) => console.log(msg, data ? sanitizeObject(data) : ''),
    error: (msg, data) => console.error(msg, data ? sanitizeObject(data) : '')
};

// SICHER: Explizites Logging nicht-sensibler Felder
app.post('/register', (req, res) => {
    logger.info('Registrierungs-Request', {
        email: req.body.email,
        username: req.body.username
        // Passwort nicht loggen!
    });

    // Registrierung verarbeiten...
});

// SICHER: Token-Behandlung ohne Logging
app.post('/login', (req, res) => {
    const token = generateToken(req.user);
    logger.info('Login erfolgreich', { userId: req.user.id });
    // Token NICHT geloggt

    res.json({ token });
});

// SICHER: Bereinigtes Fehler-Logging
app.use((err, req, res, next) => {
    logger.error('Request-Fehler', {
        error: err.message,
        path: req.path,
        method: req.method,
        userId: req.user?.id
        // Request-Body nicht loggen
    });
    res.status(500).send('Fehler');
});

Ausgenutzt in der Praxis

Twitter Interne Logs (2018)

Twitter entdeckte, dass Passwörter im Klartext in interne Systeme geloggt wurden. Obwohl nicht extern ausgenutzt, verstieß dies gegen Sicherheits-Best-Practices und erforderte Massen-Passwort-Resets.

GitHub OAuth-Tokens (2018)

GitHub loggte versehentlich OAuth-Tokens im Klartext, was den Widerruf potenziell kompromittierter Tokens erforderte.

Facebook Passwort-Logging (2019)

Facebook speicherte Hunderte Millionen Benutzerpasswörter in Klartext-Logs, die für Mitarbeiter zugänglich waren, entdeckt während interner Sicherheitsüberprüfung.


Tools zum Testen/Ausnutzen


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-532: Insertion of Sensitive Information into Log File." https://cwe.mitre.org/data/definitions/532.html

  2. OWASP. "Logging Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html