Einschluss sensibler Informationen in Include-Datei

Beschreibung

Einschluss sensibler Informationen in Include-Datei ist eine Schwachstelle, bei der sensible Daten in Dateien gespeichert werden, die dafür vorgesehen sind, von anderen Quelldateien eingebunden zu werden, diese Include-Dateien aber für Angreifer direkt zugänglich werden können. Include-Dateien (wie .inc, .conf oder Header-Dateien) enthalten oft Datenbank-Anmeldedaten, API-Schlüssel, Konfigurationseinstellungen und andere sensible Informationen. Wenn Webserver falsch konfiguriert sind und diese Dateien als Klartext bereitstellen, anstatt sie durch den entsprechenden Interpreter zu verarbeiten, oder wenn diese Dateien in öffentlich zugänglichen Verzeichnissen platziert werden, können Angreifer sie abrufen und die eingebetteten sensiblen Daten extrahieren.

Risiko

Include-Dateien mit sensiblen Informationen erzeugen kritische Sicherheitsrisiken. Dateien mit Erweiterungen wie .inc werden möglicherweise von Webservern nicht als Code erkannt und als Klartext bereitgestellt, was ihren Inhalt offenlegt. Konfigurations-Include-Dateien enthalten oft Datenbank-Anmeldedaten, die Angreifern direkten Datenbankzugriff ermöglichen. API-Schlüssel in Include-Dateien ermöglichen unbefugten Service-Zugriff. Hartcodierte Passwörter umgehen die normale Authentifizierung vollständig. Selbst wenn die Include-Dateien nicht direkt zugänglich sind, können Quellcode-Lecks, Backup-Dateien oder Versionskontroll-Offenlegungen ihren Inhalt enthüllen. Die zentralisierte Natur von Include-Dateien bedeutet, dass eine offengelegte Datei oft mehrere Systemkomponenten kompromittiert.

Lösung

Speichern Sie niemals sensible Informationen direkt in Include-Dateien. Verwenden Sie Umgebungsvariablen oder sichere Konfigurationsverwaltungssysteme für Anmeldedaten. Platzieren Sie Include-Dateien außerhalb des web-zugänglichen Document-Roots. Konfigurieren Sie Webserver, um den Zugriff auf Include-Dateierweiterungen (.inc, .conf, .ini) zu verweigern. Verwenden Sie PHP-, ASP- oder andere serverseitige Erweiterungen für Include-Dateien, damit sie verarbeitet statt als Text bereitgestellt werden. Implementieren Sie ordnungsgemäße Zugriffskontrollen auf Konfigurationsverzeichnisse. Verwenden Sie Geheimisverwaltungslösungen wie HashiCorp Vault für sensible Konfiguration. Prüfen Sie Include-Dateien regelmäßig auf sensible Datenoffenlegung.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Angreifer können auf sensible Informationen zugreifen, die in Include-Dateien eingebettet sind, einschließlich Anmeldedaten, API-Schlüssel, Datenbankverbindungsstrings und Systemkonfigurationsdetails.

Beispielcode + Lösungscode

Verwundbarer Code

<?php
// Verwundbar: database.inc mit Anmeldedaten (als Klartext zugänglich)
// Datei: /var/www/html/includes/database.inc

// Verwundbar: Anmeldedaten in Include-Datei
$dbName = 'production_users';
$dbHost = 'db.company.internal';
$dbUser = 'app_admin';
$dbPassword = 'Pr0duct1on_P@ssw0rd!';

// Verwundbar: API-Schlüssel in Include-Datei
$stripeKey = 'sk_live_51ABC123XYZ789';
$awsAccessKey = 'AKIAIOSFODNN7EXAMPLE';
$awsSecretKey = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';

// Wenn .inc-Erweiterung nicht von PHP behandelt wird, wird diese Datei als Klartext bereitgestellt
// Anfrage: GET /includes/database.inc
// Antwort: Alle Anmeldedaten sichtbar!
?>
<?php
// Verwundbar: config.inc.php die trotzdem zugänglich ist
// Datei: /var/www/html/includes/config.inc.php

// Verwundbar: Selbst mit .php-Erweiterung kann sie offengelegt werden durch:
// - Verzeichnisauflistung
// - Backup-Dateien (config.inc.php.bak)
// - Versionskontroll-Offenlegung (.git)

define('DB_HOST', 'mysql.company.com');
define('DB_USER', 'root');
define('DB_PASS', 'RootPassword123!');
define('DB_NAME', 'customer_data');

// Verwundbar: Verschlüsselungsschlüssel
define('ENCRYPTION_KEY', '32-character-encryption-key-here');
define('JWT_SECRET', 'jwt-signing-secret-2024');

// Verwundbar: Drittanbieter-Service-Anmeldedaten
define('SMTP_PASSWORD', 'mail_server_password');
define('PAYMENT_API_SECRET', 'payment_processor_secret');
?>
// Verwundbar: C-Header-Datei mit hartcodierten Anmeldedaten
// Datei: config.h (kann in öffentlichem Repository oder offengelegt sein)

#ifndef CONFIG_H
#define CONFIG_H

// Verwundbar: Datenbank-Anmeldedaten im Header
#define DB_HOST "192.168.1.100"
#define DB_PORT 3306
#define DB_USER "admin"
#define DB_PASSWORD "AdminPassword123"
#define DB_NAME "production"

// Verwundbar: API-Schlüssel
#define API_KEY "sk_live_production_key_12345"
#define API_SECRET "api_secret_value_67890"

// Verwundbar: Verschlüsselungsparameter
#define ENCRYPTION_KEY "AES256EncryptionKeyValue"
#define ENCRYPTION_IV "InitializationVector"

#endif
<!-- Verwundbar: ASP-Include-Datei mit Anmeldedaten -->
<!-- Datei: /includes/connection.inc -->

<%
' Verwundbar: Datenbankverbindungsstring mit Anmeldedaten
Dim connectionString
connectionString = "Provider=SQLOLEDB;Data Source=sql.company.com;" & _
                  "Initial Catalog=Production;User ID=sa;Password=SysAdmin123!"

' Verwundbar: LDAP-Anmeldedaten
Dim ldapUser, ldapPassword
ldapUser = "cn=admin,dc=company,dc=com"
ldapPassword = "LDAPAdminPassword"

' Verwundbar: Dienstkonto-Anmeldedaten
Dim serviceAccount, servicePassword
serviceAccount = "DOMAIN\ServiceAccount"
servicePassword = "Service@ccount123"
%>
# Verwundbar: Python-Config-Include mit Geheimnissen
# Datei: config/settings.inc.py (kann von falsch konfiguriertem Server bereitgestellt werden)

# Verwundbar: Datenbankeinstellungen
DATABASE = {
    'host': 'db.company.internal',
    'port': 5432,
    'name': 'production',
    'user': 'postgres',
    'password': 'PostgresPassword123!'
}

# Verwundbar: Geheime Schlüssel
SECRET_KEY = 'django-insecure-very-secret-key-here'
API_KEY = 'production-api-key-value'

# Verwundbar: Drittanbieter-Anmeldedaten
AWS_ACCESS_KEY = 'AKIAIOSFODNN7EXAMPLE'
AWS_SECRET_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
STRIPE_SECRET = 'sk_live_production_stripe_key'

Lösungscode

<?php
// Behoben: Datenbankkonfiguration mit Umgebungsvariablen
// Datei: /var/www/html/includes/database.php

// Behoben: Keine Anmeldedaten in Datei, aus Umgebung laden
function getDatabaseConfig() {
    return [
        'host' => getenv('DB_HOST'),
        'name' => getenv('DB_NAME'),
        'user' => getenv('DB_USER'),
        'password' => getenv('DB_PASSWORD')
    ];
}

// Behoben: Konfiguration validieren
function validateDatabaseConfig($config) {
    foreach (['host', 'name', 'user', 'password'] as $key) {
        if (empty($config[$key])) {
            error_log("Datenbankkonfiguration fehlt: $key");
            throw new Exception("Konfigurationsfehler");
        }
    }
    return $config;
}

// Behoben: API-Schlüssel aus Umgebung
function getApiKey($service) {
    $key = getenv(strtoupper($service) . '_API_KEY');
    if (empty($key)) {
        throw new Exception("API-Schlüssel nicht konfiguriert für: $service");
    }
    return $key;
}
?>
<?php
// Behoben: Sicherer Konfigurations-Loader
// Datei: /var/www/app/config/config.php (außerhalb Webroot)

class SecureConfig {
    private static $instance = null;
    private $config = [];

    private function __construct() {
        // Behoben: Aus Umgebungsvariablen laden
        $this->loadFromEnvironment();

        // Behoben: Oder aus sicherer Datei außerhalb Webroot laden
        $this->loadFromSecureFile();
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function loadFromEnvironment() {
        $this->config['db'] = [
            'host' => getenv('DB_HOST'),
            'user' => getenv('DB_USER'),
            'password' => getenv('DB_PASSWORD'),
            'name' => getenv('DB_NAME')
        ];

        $this->config['encryption_key'] = getenv('ENCRYPTION_KEY');
        $this->config['jwt_secret'] = getenv('JWT_SECRET');
    }

    private function loadFromSecureFile() {
        // Behoben: Konfigurationsdatei außerhalb Webroot
        $configFile = '/etc/app/secrets.json';

        if (file_exists($configFile)) {
            $data = json_decode(file_get_contents($configFile), true);
            if ($data) {
                $this->config = array_merge($this->config, $data);
            }
        }
    }

    public function get($key, $default = null) {
        return $this->config[$key] ?? $default;
    }
}
?>
# Behoben: Apache-Konfiguration zum Blockieren von Include-Dateien
# /etc/apache2/sites-available/example.conf

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html

    # Behoben: Zugriff auf Include-Dateierweiterungen verweigern
    <FilesMatch "\.(inc|conf|ini|cfg|bak|old)$">
        Require all denied
    </FilesMatch>

    # Behoben: Zugriff auf Konfigurationsverzeichnisse verweigern
    <DirectoryMatch "/(config|includes|conf)/">
        Require all denied
    </DirectoryMatch>

    # Behoben: Backup-Dateien blockieren
    <FilesMatch "(~|\.bak|\.old|\.orig|\.swp)$">
        Require all denied
    </FilesMatch>
</VirtualHost>
// Behoben: C-Konfiguration aus sicherer Quelle geladen
// Datei: config.h

#ifndef CONFIG_H
#define CONFIG_H

// Behoben: Keine hartcodierten Anmeldedaten, nur Strukturdefinitionen
typedef struct {
    char host[256];
    int port;
    char user[64];
    char password[128];
    char database[64];
} DatabaseConfig;

typedef struct {
    char api_key[256];
    char api_secret[256];
} ApiConfig;

// Behoben: Funktionen zum sicheren Laden der Konfiguration
int load_database_config(DatabaseConfig* config);
int load_api_config(ApiConfig* config);

// Behoben: Konfiguration aus Umgebung oder sicherer Datei geladen
// Implementierung in config.c, nicht im Header offengelegt

#endif
// Behoben: config.c - sicheres Konfigurationsladen
#include "config.h"
#include <stdlib.h>
#include <string.h>

int load_database_config(DatabaseConfig* config) {
    // Behoben: Aus Umgebungsvariablen laden
    const char* host = getenv("DB_HOST");
    const char* user = getenv("DB_USER");
    const char* password = getenv("DB_PASSWORD");
    const char* database = getenv("DB_NAME");
    const char* port_str = getenv("DB_PORT");

    if (!host || !user || !password || !database) {
        return -1; // Konfiguration fehlt
    }

    strncpy(config->host, host, sizeof(config->host) - 1);
    strncpy(config->user, user, sizeof(config->user) - 1);
    strncpy(config->password, password, sizeof(config->password) - 1);
    strncpy(config->database, database, sizeof(config->database) - 1);
    config->port = port_str ? atoi(port_str) : 3306;

    return 0;
}
# Behoben: Python sichere Konfiguration
# Datei: config/settings.py

import os
from functools import lru_cache

class SecureConfig:
    """Konfiguration aus Umgebung geladen, niemals hartcodiert."""

    @staticmethod
    @lru_cache()
    def get_database_config():
        """Datenbankkonfiguration aus Umgebung laden."""
        config = {
            'host': os.environ.get('DB_HOST'),
            'port': int(os.environ.get('DB_PORT', 5432)),
            'name': os.environ.get('DB_NAME'),
            'user': os.environ.get('DB_USER'),
            'password': os.environ.get('DB_PASSWORD')
        }

        # Behoben: Erforderliche Felder validieren
        required = ['host', 'name', 'user', 'password']
        missing = [k for k in required if not config.get(k)]
        if missing:
            raise ValueError(f"Fehlende Datenbankkonfiguration: {missing}")

        return config

    @staticmethod
    def get_secret_key():
        """Anwendungsgeheimschlüssel aus Umgebung abrufen."""
        key = os.environ.get('SECRET_KEY')
        if not key:
            raise ValueError("SECRET_KEY Umgebungsvariable erforderlich")
        return key

    @staticmethod
    def get_api_key(service):
        """API-Schlüssel für einen Service aus Umgebung abrufen."""
        key_name = f'{service.upper()}_API_KEY'
        key = os.environ.get(key_name)
        if not key:
            raise ValueError(f"API-Schlüssel nicht konfiguriert: {key_name}")
        return key


# Behoben: Verwendung
# database_config = SecureConfig.get_database_config()
# stripe_key = SecureConfig.get_api_key('stripe')
# Behoben: Docker Compose mit Geheimisverwaltung
version: '3.8'

services:
  app:
    build: .
    environment:
      - DB_HOST=db
      - DB_NAME=production
      - DB_USER_FILE=/run/secrets/db_user
      - DB_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_user
      - db_password
      - api_key

secrets:
  db_user:
    external: true
  db_password:
    external: true
  api_key:
    external: true

CVE-Beispiele

  • CVE-2018-1000130: Jolokia enthielt sensible Informationen in einer Include-Datei, die offengelegt werden könnte.
  • CVE-2016-9566: Nagios XI enthielt sensible Datenbank-Anmeldedaten in einer Konfigurations-Include-Datei.
  • CVE-2012-0867: PostgreSQL legte Anmeldedaten durch Include-Datei-Fehlkonfiguration offen.

Referenzen

  1. MITRE Corporation. "CWE-541: Inclusion of Sensitive Information in an Include File." https://cwe.mitre.org/data/definitions/541.html
  2. OWASP. "Secrets Management Cheat Sheet."
  3. OWASP. "OWASP Top Ten 2021 - A05:2021 Security Misconfiguration."