J2EE-Fehlkonfiguration: Klartext-Passwort in Konfigurationsdatei

Beschreibung

J2EE-Fehlkonfiguration: Klartext-Passwort in Konfigurationsdatei ist eine Schwachstelle, bei der eine Java-Enterprise-Edition-Anwendung Passwörter im Klartext in Konfigurationsdateien speichert. Diese Konfigurationsdateien, einschließlich Properties-Dateien, XML-Konfiguration und Deployment-Deskriptoren, enthalten oft Datenbank-Anmeldeinformationen, LDAP-Passwörter, Dienstkonto-Anmeldeinformationen und API-Schlüssel ohne Verschlüsselung oder Schutz. Jeder, der Zugang zum Dateisystem oder Konfigurationsmanagementsystem erhält, kann diese Anmeldeinformationen lesen und für den Zugriff auf geschützte Ressourcen verwenden.

Risiko

Das Speichern von Klartext-Passwörtern in Konfigurationsdateien erzeugt schwerwiegende Sicherheitsrisiken. Konfigurationsdateien werden häufig gesichert, in der Versionskontrolle versioniert, während Deployments kopiert und sind für Systemadministratoren und Entwickler zugänglich. Eine einzelne exponierte Konfigurationsdatei kann Datenbankserver, Verzeichnisdienste, E-Mail-Systeme und andere kritische Infrastruktur kompromittieren. Angreifer, die Lesezugriff auf das Dateisystem durch Verzeichnisdurchlauf, Backup-Exposition oder Quellcode-Lecks erlangen, erhalten sofort funktionierende Anmeldeinformationen.

Lösung

Speichern Sie niemals Klartext-Passwörter in Konfigurationsdateien. Verwenden Sie branchenübliche Verschlüsselung zum Schutz sensibler Anmeldeinformationen vor dem Speichern. Implementieren Sie ordnungsgemäßes Schlüsselmanagement für Verschlüsselungsschlüssel getrennt von den verschlüsselten Daten. Verwenden Sie sichere Credential-Vaults wie HashiCorp Vault, AWS Secrets Manager oder Azure Key Vault. Nutzen Sie Container-Orchestrierungs-Secrets-Management in Kubernetes oder Docker Swarm. Verwenden Sie Umgebungsvariablen für sensible Daten anstelle von Konfigurationsdateien.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Exponierte Anmeldeinformationen ermöglichen Angreifern, Authentifizierung zu umgehen und unbefugten Zugriff auf Datenbanken, LDAP-Verzeichnisse und andere geschützte Ressourcen zu erlangen.

Beispielcode

Verwundbarer Code

# Verwundbar: database.properties mit Klartext-Anmeldeinformationen
# Datei: /WEB-INF/classes/database.properties

# Verwundbar: Datenbank-Anmeldeinformationen im Klartext
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://db.company.internal:3306/production
db.username=app_admin
db.password=Pr0duct1on_P@ssw0rd_2024!

# Verwundbar: Connection-Pool-Einstellungen mit Anmeldeinformationen
pool.maxActive=100
pool.maxIdle=30
<!-- Verwundbar: persistence.xml mit Klartext-Anmeldeinformationen -->
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="ProductionPU">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="javax.persistence.jdbc.driver"
                      value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:mysql://db.company.internal:3306/production"/>
            <!-- Verwundbar: Klartext-Anmeldeinformationen -->
            <property name="javax.persistence.jdbc.user" value="db_admin"/>
            <property name="javax.persistence.jdbc.password"
                      value="DatabasePassword123!"/>
        </properties>
    </persistence-unit>
</persistence>
// Verwundbar: Java-Code lädt Klartext-Anmeldeinformationen
public class VerwundbarerConfigLoader {

    public DataSource getDataSource() throws Exception {
        // Verwundbar: Laden von Klartext-Anmeldeinformationen aus Properties
        Properties props = new Properties();
        props.load(getClass().getResourceAsStream("/database.properties"));

        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName(props.getProperty("db.driver"));
        ds.setUrl(props.getProperty("db.url"));
        ds.setUsername(props.getProperty("db.username"));
        ds.setPassword(props.getProperty("db.password"));  // Klartext!

        return ds;
    }

    // Verwundbar: Hartcodierte Anmeldeinformationen als Fallback
    private static final String DEFAULT_PASSWORD = "DefaultP@ss123";
}

Lösungscode

// Behoben: Sichere Anmeldeinformationsverwaltung mit Verschlüsselung
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class SichererConfigLoader {
    private final SecretKey encryptionKey;

    public SichererConfigLoader() {
        // Behoben: Verschlüsselungsschlüssel aus sicherer Quelle laden
        String keyString = System.getenv("CONFIG_ENCRYPTION_KEY");
        if (keyString == null) {
            throw new IllegalStateException("Verschlüsselungsschlüssel nicht konfiguriert");
        }
        this.encryptionKey = new SecretKeySpec(
            Base64.getDecoder().decode(keyString), "AES");
    }

    public String decryptPassword(String encryptedPassword) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, encryptionKey);
        byte[] decrypted = cipher.doFinal(
            Base64.getDecoder().decode(encryptedPassword));
        return new String(decrypted);
    }

    public DataSource getDataSource() throws Exception {
        Properties props = new Properties();
        props.load(getClass().getResourceAsStream("/database.properties"));

        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName(props.getProperty("db.driver"));
        ds.setUrl(props.getProperty("db.url"));
        ds.setUsername(props.getProperty("db.username"));

        // Behoben: Passwort vor Verwendung entschlüsseln
        String encryptedPassword = props.getProperty("db.password.encrypted");
        ds.setPassword(decryptPassword(encryptedPassword));

        return ds;
    }
}
// Behoben: Verwendung von Umgebungsvariablen für Anmeldeinformationen
public class EnvironmentConfigLoader {

    public DataSource getDataSource() {
        // Behoben: Anmeldeinformationen aus Umgebungsvariablen
        String dbUrl = getRequiredEnv("DB_URL");
        String dbUser = getRequiredEnv("DB_USERNAME");
        String dbPassword = getRequiredEnv("DB_PASSWORD");

        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl(dbUrl);
        ds.setUsername(dbUser);
        ds.setPassword(dbPassword);

        return ds;
    }

    private String getRequiredEnv(String name) {
        String value = System.getenv(name);
        if (value == null || value.isEmpty()) {
            throw new IllegalStateException(
                "Erforderliche Umgebungsvariable nicht gesetzt: " + name);
        }
        return value;
    }
}
// Behoben: Verwendung von HashiCorp Vault für Secrets
import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultConfig;

public class VaultConfigLoader {
    private final Vault vault;

    public VaultConfigLoader() throws Exception {
        // Behoben: Vault-Authentifizierung via Token oder AppRole
        VaultConfig config = new VaultConfig()
            .address(System.getenv("VAULT_ADDR"))
            .token(System.getenv("VAULT_TOKEN"))
            .build();
        this.vault = new Vault(config);
    }

    public DataSource getDataSource() throws Exception {
        // Behoben: Anmeldeinformationen von Vault abrufen
        Map<String, String> data = vault.logical()
            .read("secret/data/database/production")
            .getData();

        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl(data.get("url"));
        ds.setUsername(data.get("username"));
        ds.setPassword(data.get("password"));

        return ds;
    }
}
# Behoben: Kubernetes Secret für Anmeldeinformationen
apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
type: Opaque
stringData:
  DB_USERNAME: app_admin
  DB_PASSWORD: ProductionPassword123!

---
# Behoben: Pod referenziert Secret
apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: webapp
    image: myapp:latest
    envFrom:
    - secretRef:
        name: database-credentials

CVE-Beispiele

  • CVE-2019-3799: Spring Cloud Config Server exponierte Klartext-Anmeldeinformationen in Konfigurationsdateien.
  • CVE-2018-1000130: Jolokia exponierte sensible Konfiguration einschließlich Anmeldeinformationen.
  • CVE-2017-7657: Eclipse Jetty leakte Anmeldeinformationen durch Konfigurationsdateien.

Referenzen

  1. MITRE Corporation. "CWE-555: J2EE Misconfiguration: Plaintext Password in Configuration File." https://cwe.mitre.org/data/definitions/555.html
  2. OWASP. "Secrets Management Cheat Sheet."
  3. OWASP. "Password Storage Cheat Sheet."