Klartext-Speicherung in Datei oder auf Festplatte
Beschreibung
Klartext-Speicherung in Datei oder auf Festplatte ist eine Schwachstelle, die auftritt, wenn ein Produkt sensible Informationen im Klartext in Dateien oder direkt auf Festplattenspeicher speichert. Dies umfasst Konfigurationsdateien, Log-Dateien, Datendateien und jeglichen anderen persistenten Speicher. Sensible Informationen wie Passwörter, API-Schlüssel, Verschlüsselungsschlüssel, persönliche Daten und Anmeldedaten können von Angreifern gelesen werden, die Zugriff auf das Dateisystem erlangen - durch legitimen Zugriff, Fehlkonfiguration, Directory-Traversal-Schwachstellen oder physischen Zugriff auf Speichermedien. Selbst kodierte (nicht menschenlesbare) Informationen bieten minimalen Schutz, da Angreifer leicht die Kodierungsmethode bestimmen und die Daten dekodieren können.
Risiko
Klartext-Dateispeicherung schafft persistente Exposition sensibler Daten über mehrere Angriffsvektoren. Konfigurationsdateien mit Klartext-Anmeldedaten werden häufig durch Webserver-Fehlkonfigurationen, Backup-Lecks, Source-Code-Repository-Commits oder Directory-Traversal-Schwachstellen exponiert. Log-Dateien, die sensible Daten enthalten, können für Support-Personal, Log-Aggregationssysteme zugänglich sein oder in weniger sicheren Archivsystemen gespeichert werden. Physischer Zugriff auf Speichermedien durch gestohlene Geräte, unsachgemäße Entsorgung oder Kompromittierung von Rechenzentren exponiert alle Klartext-Daten. Entschlüsselte Kopien sensibler Daten, die temporär auf die Festplatte geschrieben werden, können auch nach dem Löschen durch die Anwendung aufgrund von Dateisystemverhalten bestehen bleiben. Die langfristige Persistenz von Dateien bedeutet, dass Expositionen Daten aus jahrelangem Betrieb betreffen können.
Lösung
Verschlüsseln Sie alle sensiblen Daten vor dem Schreiben in Dateien oder auf die Festplatte. Verwenden Sie starke Verschlüsselungsalgorithmen (AES-256) mit ordnungsgemäßem Schlüsselmanagement für Konfigurationsdatei-Secrets. Erwägen Sie die Verwendung dedizierter Secrets-Management-Lösungen (HashiCorp Vault, AWS Secrets Manager) anstelle dateibasierter Credential-Speicherung. Implementieren Sie Umgebungsvariablen oder verschlüsselte Konfiguration für Datenbank-Anmeldedaten und API-Schlüssel. Implementieren Sie für Log-Dateien Filterung oder Maskierung, um zu verhindern, dass sensible Daten protokolliert werden. Konfigurieren Sie ordnungsgemäße Dateiberechtigungen, die den Zugriff auf notwendige Konten beschränken. Verwenden Sie Vollverschlüsselung als zusätzliche Schutzschicht. Stellen Sie sicher, dass temporäre Dateien mit sensiblen Daten mit sicheren Löschmethoden gelöscht werden. Auditieren Sie Dateispeicherorte auf Exposition sensibler Daten.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Angreifer mit Dateisystemzugriff können sensible Informationen direkt lesen. Physischer oder administrativer Zugriff auf Rohspeicher exponiert alle Klartext-Daten unabhängig von Dateiberechtigungen. |
Beispielcode
Anfälliger Code (Java/ASP.NET)
Die folgenden Beispiele demonstrieren Klartext-Dateispeicherungs-Schwachstellen:
// Anfällig: Java-Properties-Datei mit Klartext-Anmeldedaten
// webapp.properties Dateiinhalt:
// webapp.ldap.username=secretUsername
// webapp.ldap.password=secretPassword
// db.connection.password=DatabasePass123
import java.io.FileOutputStream;
import java.util.Properties;
public class VulnerableConfigWriter {
public void saveConfiguration(String dbHost, String dbUser, String dbPassword) {
Properties props = new Properties();
props.setProperty("db.host", dbHost);
props.setProperty("db.user", dbUser);
// Anfällig: Klartext-Passwort in Properties-Datei
props.setProperty("db.password", dbPassword);
try (FileOutputStream out = new FileOutputStream("config.properties")) {
props.store(out, "Datenbank-Konfiguration");
} catch (Exception e) {
e.printStackTrace();
}
}
// Anfällig: Entschlüsselte Daten in Datei schreiben
public void processEncryptedFile(String encryptedFile, String outputFile) {
byte[] encrypted = readFile(encryptedFile);
byte[] decrypted = decrypt(encrypted);
// Anfällig: Entschlüsselte sensible Daten auf Festplatte geschrieben!
writeFile(outputFile, decrypted);
// Auch wenn später gelöscht, können Daten wiederherstellbar sein
}
}
<!-- Anfällig: ASP.NET-Konfiguration mit Klartext-Anmeldedaten -->
<!-- web.config -->
<configuration>
<connectionStrings>
<!-- Anfällig: Klartext-Datenbank-Anmeldedaten -->
<add name="ProductionDB"
connectionString="Server=prod-db.example.com;Database=AppDB;uid=admin;pwd=SuperSecretPassword123;"
providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<!-- Anfällig: API-Schlüssel im Klartext -->
<add key="Stripe.ApiKey" value="sk_live_abcd1234efgh5678" />
<add key="AWS.SecretKey" value="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" />
</appSettings>
</configuration>
# Anfällig: Python schreibt Anmeldedaten in Datei
import json
def vulnerable_save_config(config_dict, filepath):
# Anfällig: Klartext-Anmeldedaten in JSON-Datei
config = {
'database': {
'host': config_dict['db_host'],
'user': config_dict['db_user'],
'password': config_dict['db_password'], # Klartext!
'port': 5432
},
'api_keys': {
'stripe': config_dict['stripe_key'], # Klartext!
'sendgrid': config_dict['sendgrid_key']
}
}
with open(filepath, 'w') as f:
json.dump(config, f, indent=2)
# Dateiberechtigungen sind oft standardmäßig weltweit lesbar!
# Anfällig: Sensible Daten in Datei protokollieren
def vulnerable_log_request(request_data, logfile):
log_entry = {
'timestamp': datetime.now().isoformat(),
'user': request_data.get('username'),
'password': request_data.get('password'), # Klartext-Passwort im Log!
'credit_card': request_data.get('card_number'),
'action': request_data.get('action')
}
with open(logfile, 'a') as f:
f.write(json.dumps(log_entry) + '\n')
Korrigierter Code (Java/ASP.NET)
// Korrigiert: Verschlüsselte Konfigurationsspeicherung
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.*;
import java.io.*;
public class SecureConfigWriter {
private final SecretKey encryptionKey;
public SecureConfigWriter(SecretKey key) {
this.encryptionKey = key;
}
public void saveConfiguration(String dbHost, String dbUser, String dbPassword) {
Properties props = new Properties();
props.setProperty("db.host", dbHost);
props.setProperty("db.user", dbUser);
// Korrigiert: Sensible Werte verschlüsseln
props.setProperty("db.password.encrypted", encryptValue(dbPassword));
try (FileOutputStream out = new FileOutputStream("config.properties")) {
props.store(out, "Datenbank-Konfiguration");
} catch (Exception e) {
throw new RuntimeException("Konfiguration könnte nicht gespeichert werden", e);
}
// Korrigiert: Restriktive Dateiberechtigungen setzen
setRestrictivePermissions("config.properties");
}
private String encryptValue(String value) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
SecureRandom.getInstanceStrong().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(128, iv));
byte[] encrypted = cipher.doFinal(value.getBytes());
byte[] combined = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
return Base64.getEncoder().encodeToString(combined);
} catch (Exception e) {
throw new RuntimeException("Verschlüsselung fehlgeschlagen", e);
}
}
// Korrigiert: Verschlüsselte Daten nur im Speicher verarbeiten
public byte[] processEncryptedFile(String encryptedFile) {
byte[] encrypted = readFile(encryptedFile);
byte[] decrypted = decrypt(encrypted);
// Korrigiert: Im Speicher verarbeiten, niemals entschlüsselt auf Festplatte schreiben
byte[] result = processData(decrypted);
// Korrigiert: Sensible Daten aus dem Speicher löschen
Arrays.fill(decrypted, (byte) 0);
return result;
}
}
// Korrigiert: Umgebungsvariablen für Anmeldedaten verwenden
public class SecureConfiguration {
public String getDatabasePassword() {
// Korrigiert: Aus Umgebung lesen, nicht aus Datei
String password = System.getenv("DB_PASSWORD");
if (password == null) {
throw new IllegalStateException("DB_PASSWORD nicht gesetzt");
}
return password;
}
// Korrigiert: Secrets Manager verwenden
public String getApiKey(String keyName) {
SecretsManager client = SecretsManagerClient.create();
GetSecretValueRequest request = GetSecretValueRequest.builder()
.secretId(keyName)
.build();
return client.getSecretValue(request).secretString();
}
}
<!-- Korrigiert: ASP.NET mit verschlüsselter Konfiguration -->
<!-- web.config -->
<configuration>
<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
<!-- Korrigiert: Connection-Strings mit DPAPI verschlüsselt -->
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE...</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
<!-- Korrigiert: Azure Key Vault oder ähnliches für Secrets verwenden -->
<appSettings>
<add key="Stripe.ApiKey" value="@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/StripeKey)" />
</appSettings>
</configuration>
<!-- Zum Verschlüsseln: aspnet_regiis -pe "connectionStrings" -app "/MyApp" -->
# Korrigiert: Sichere Konfigurationshandhabung
import os
import json
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class SecureConfigManager:
def __init__(self, encryption_key):
self.aesgcm = AESGCM(encryption_key)
def save_config(self, config_dict, filepath):
# Korrigiert: Nicht-sensible und sensible Daten trennen
public_config = {
'database': {
'host': config_dict['db_host'],
'port': 5432
}
}
# Korrigiert: Sensible Werte verschlüsseln
sensitive = {
'db_user': config_dict['db_user'],
'db_password': config_dict['db_password'],
'api_keys': config_dict.get('api_keys', {})
}
nonce = os.urandom(12)
encrypted = self.aesgcm.encrypt(
nonce,
json.dumps(sensitive).encode(),
None
)
public_config['encrypted_secrets'] = base64.b64encode(
nonce + encrypted
).decode()
with open(filepath, 'w') as f:
json.dump(public_config, f)
# Korrigiert: Dateiberechtigungen einschränken
os.chmod(filepath, 0o600)
# Korrigiert: Umgebungsvariablen verwenden
def get_secure_config():
return {
'db_host': os.environ.get('DB_HOST', 'localhost'),
'db_user': os.environ.get('DB_USER'),
'db_password': os.environ.get('DB_PASSWORD'), # Aus Umgebung
'api_keys': {
'stripe': os.environ.get('STRIPE_API_KEY')
}
}
# Korrigiert: Sicheres Logging ohne sensible Daten
def secure_log_request(request_data, logfile):
log_entry = {
'timestamp': datetime.now().isoformat(),
'user': request_data.get('username'),
'password': '[ZENSIERT]', # Niemals Passwörter protokollieren
'credit_card': mask_card(request_data.get('card_number')),
'action': request_data.get('action')
}
with open(logfile, 'a') as f:
f.write(json.dumps(log_entry) + '\n')
def mask_card(card_number):
if card_number and len(card_number) >= 4:
return '*' * (len(card_number) - 4) + card_number[-4:]
return '[ZENSIERT]'
Die Korrektur verschlüsselt sensible Daten vor der Speicherung, verwendet Umgebungsvariablen oder Secrets Manager und beschränkt Dateiberechtigungen.
Ausgenutzt in der Praxis
Weltweit lesbare Credential-Dateien (Verschiedene Systeme, 2001)
CVE-2001-1481 dokumentierte Systeme, die Anmeldedaten im Klartext in weltweit lesbaren Dateien speicherten, was jedem lokalen Benutzer ermöglichte, sensible Anmeldedaten zu lesen.
Konfigurationsdatei-Passwortexposition (Enterprise-Anwendungen, 2005)
CVE-2005-1828 und CVE-2005-2209 dokumentierten Enterprise-Anwendungen, die Datenbank- und Administratorpasswörter in Klartext-Konfigurationsdateien speicherten.
Privater Schlüssel in Log-Datei (Sicherheitssoftware, 2004)
CVE-2004-2397 dokumentierte Sicherheitssoftware, die Klartext-private-Schlüssel und Passphrasen in Log-Dateien schrieb.
Tools zum Testen/Ausnutzen
-
TruffleHog — Scannt Dateisysteme und Repositories nach Secrets.
-
GitLeaks — Erkennt Secrets in Dateien und Git-Historie.
-
Grep — Einfaches Mustermatch für gängige Credential-Muster in Dateien.
CVE-Beispiele
-
CVE-2001-1481 — Klartext-Anmeldedaten in weltweit lesbarer Datei.
-
CVE-2005-1828 — Passwort im Klartext in Konfigurationsdatei.
-
CVE-2005-2209 — Passwort im Klartext in Konfigurationsdatei.
-
CVE-2002-1696 — Entschlüsselte Nachrichtenkopie auf Festplatte geschrieben.
-
CVE-2004-2397 — Klartext privater Schlüssel in Log-Datei.
Referenzen
-
MITRE Corporation. "CWE-313: Cleartext Storage in a File or on Disk." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/313.html
-
OWASP Foundation. "Cryptographic Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
-
OWASP Foundation. "Logging Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html