Einfügen sensibler Informationen in extern zugängliche Datei oder Verzeichnis

Beschreibung

Einfügen sensibler Informationen in extern zugängliche Datei oder Verzeichnis tritt auf, wenn Software sensible Informationen in Dateien oder Verzeichnisse platziert, die für unbefugte Akteure zugänglich sind. Dies umfasst das Speichern sensibler Daten in web-zugänglichen Verzeichnissen, öffentlich lesbaren Logdateien, Backup-Dateien in Document-Roots, temporären Dateien mit unsachgemäßen Berechtigungen oder Konfigurationsdateien, die über das Web zugänglich sind. Selbst verschlüsselte sensible Daten an zugänglichen Speicherorten können anfällig für Offline-Angriffe sein.

Risiko

Sensible Dateien an zugänglichen Speicherorten sind eine häufige Quelle von Datenverletzungen. Backup-Dateien (.bak, .old, .sql) in Webverzeichnissen legen Quellcode und Datenbankinhalte offen. Logdateien können Anmeldedaten, Sitzungstokens oder persönliche Daten enthalten. Konfigurationsdateien enthalten oft Datenbank-Anmeldedaten, API-Schlüssel und Verschlüsselungsgeheimnisse. Git-Repositories (.git-Verzeichnisse) in Web-Roots legen die gesamte Versionshistorie offen. Diese Probleme werden leicht durch Verzeichnis-Enumeration oder Suchmaschinen-Dorking entdeckt.

Lösung

Speichern Sie niemals sensible Dateien in web-zugänglichen Verzeichnissen. Konfigurieren Sie Webserver, um den Zugriff auf sensible Dateitypen zu verweigern (.sql, .bak, .log, .git, .env). Setzen Sie ordnungsgemäße Dateiberechtigungen, die den Zugriff auf autorisierte Benutzer beschränken. Verwenden Sie separate Speicherorte für sensible Daten außerhalb des Web-Roots. Implementieren Sie ordnungsgemäße Zugriffskontrollen auf Logverzeichnisse. Entfernen Sie Backup- und temporäre Dateien von Produktionssystemen. Verwenden Sie .gitignore und deployen Sie ohne .git-Verzeichnisse. Prüfen Sie regelmäßig zugängliche Verzeichnisse auf sensible Inhalte.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Datenoffenlegung

Sensible Daten einschließlich Anmeldedaten, PII und Geschäftsinformationen werden offengelegt.
AuthentifizierungBereich: Anmeldedaten-Diebstahl

Offengelegte Konfigurationsdateien offenbaren Datenbank- und API-Anmeldedaten.
IntegritätBereich: Quellcode-Offenlegung

Backup-Dateien und .git-Verzeichnisse legen Anwendungsquellcode offen.

Beispielcode + Lösungscode

Verwundbare Szenarien

# VERWUNDBAR: Apache-Konfiguration erlaubt Zugriff auf sensible Dateien
# Keine Einschränkungen für Dateitypen

DocumentRoot /var/www/html

# Diese Dateien sind zugänglich:
# /var/www/html/config.php.bak
# /var/www/html/database.sql
# /var/www/html/.env
# /var/www/html/.git/
# /var/www/html/debug.log
# VERWUNDBAR: Schreiben sensibler Daten in web-zugängliches Verzeichnis
from flask import Flask
import os

app = Flask(__name__)

# Logs im Web-Root!
LOG_FILE = '/var/www/html/app.log'

def log_request(request):
    with open(LOG_FILE, 'a') as f:
        # Logs enthalten sensible Daten!
        f.write(f"Benutzer: {request.headers.get('Authorization')}\n")
        f.write(f"Sitzung: {request.cookies.get('session')}\n")

# VERWUNDBAR: Backup-Datei im Webverzeichnis
def backup_database():
    import subprocess
    # Backup-Datei über Web zugänglich!
    subprocess.run([
        'mysqldump', 'mydb',
        '-o', '/var/www/html/backup/database.sql'
    ])

# VERWUNDBAR: Temp-Datei mit sensiblen Daten in /tmp
def process_upload(file_data, user_password):
    temp_path = f'/tmp/upload_{user_password}.tmp'  # Passwort im Dateinamen!
    with open(temp_path, 'wb') as f:
        f.write(file_data)
    # Dateiberechtigungen nicht gesetzt - weltweit lesbar!
// VERWUNDBAR: Java schreibt an zugänglichen Speicherort
public class VerwundbaresFileHandling {

    // Logdatei im Webverzeichnis
    private static final String LOG_PATH = "/var/www/html/logs/app.log";

    public void logSensitiveOperation(String userId, String action, String data) {
        try (FileWriter fw = new FileWriter(LOG_PATH, true)) {
            // Sensible Daten in web-zugänglichem Log!
            fw.write(String.format("%s - Benutzer %s: %s - Daten: %s%n",
                LocalDateTime.now(), userId, action, data));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // VERWUNDBAR: Export in zugängliches Verzeichnis
    public void exportUserData(List<User> users) throws IOException {
        String exportPath = "/var/www/html/exports/users.csv";

        try (PrintWriter pw = new PrintWriter(exportPath)) {
            for (User user : users) {
                // PII in web-zugänglicher Datei!
                pw.println(String.format("%s,%s,%s,%s",
                    user.getName(), user.getEmail(), user.getSSN(), user.getCreditCard()));
            }
        }
    }
}
// VERWUNDBAR: Node.js schreibt in öffentliches Verzeichnis
const fs = require('fs');
const path = require('path');

// Log in öffentliches Verzeichnis
const LOG_PATH = path.join(__dirname, 'public', 'logs', 'app.log');

function logRequest(req) {
    const logEntry = JSON.stringify({
        timestamp: new Date(),
        url: req.url,
        headers: req.headers,  // Enthält Auth-Tokens!
        body: req.body         // Kann Passwörter enthalten!
    });

    fs.appendFileSync(LOG_PATH, logEntry + '\n');
}

// VERWUNDBAR: Uploads in öffentlichem Ordner mit Originalnamen speichern
app.post('/upload', (req, res) => {
    const file = req.files.document;

    // An öffentlich zugänglichem Speicherort gespeichert!
    const uploadPath = path.join(__dirname, 'public', 'uploads', file.name);

    file.mv(uploadPath, (err) => {
        if (err) return res.status(500).send(err);
        res.send('Hochgeladen nach: ' + uploadPath);
    });
});

// VERWUNDBAR: Debug-Datei im Web-Root
function dumpDebugInfo() {
    const debugData = {
        env: process.env,  // Alle Umgebungsvariablen!
        config: require('./config'),
        dbConnection: db.connection.config
    };

    fs.writeFileSync('./public/debug.json', JSON.stringify(debugData, null, 2));
}

Lösungscode

# SICHER: Apache-Konfiguration blockiert sensible Dateien
DocumentRoot /var/www/html

# Zugriff auf sensible Dateitypen blockieren
<FilesMatch "\.(sql|bak|old|log|env|git|config|ini|yml|yaml)$">
    Require all denied
</FilesMatch>

# Zugriff auf versteckte Dateien und Verzeichnisse blockieren
<DirectoryMatch "/\.">
    Require all denied
</DirectoryMatch>

# Häufige sensible Verzeichnisse blockieren
<DirectoryMatch "(\.git|\.svn|\.hg|node_modules|vendor)">
    Require all denied
</DirectoryMatch>

# Spezifische Blockierungen
<Files ".env">
    Require all denied
</Files>

<Files "*.sql">
    Require all denied
</Files>
# SICHER: Nginx-Konfiguration
server {
    root /var/www/html;

    # Sensible Dateien blockieren
    location ~* \.(sql|bak|old|log|env|config|ini)$ {
        deny all;
        return 404;
    }

    # Versteckte Dateien/Verzeichnisse blockieren
    location ~ /\. {
        deny all;
        return 404;
    }

    # Git-Verzeichnis blockieren
    location ~ /\.git {
        deny all;
        return 404;
    }
}
# SICHER: Schreiben an nicht-web-zugängliche Speicherorte
from flask import Flask
import os
import tempfile

app = Flask(__name__)

# Logs außerhalb Web-Root mit ordnungsgemäßen Berechtigungen
LOG_DIR = '/var/log/myapp'  # Nicht im Web-Root!
LOG_FILE = os.path.join(LOG_DIR, 'app.log')

def setup_logging():
    os.makedirs(LOG_DIR, exist_ok=True)
    os.chmod(LOG_DIR, 0o750)  # Nur Eigentümer und Gruppe

def log_request_safe(request):
    # Keine sensiblen Header protokollieren
    safe_log = {
        'timestamp': datetime.utcnow().isoformat(),
        'method': request.method,
        'path': request.path,
        'ip': request.remote_addr
        # Keine Auth-Header, Cookies oder Body!
    }

    with open(LOG_FILE, 'a') as f:
        f.write(json.dumps(safe_log) + '\n')

# SICHER: Backup an sicherem Speicherort
def backup_database_safe():
    import subprocess

    backup_dir = '/var/backups/myapp'  # Nicht web-zugänglich!
    os.makedirs(backup_dir, exist_ok=True)
    os.chmod(backup_dir, 0o700)  # Nur Eigentümer

    backup_file = os.path.join(backup_dir, f'db_{datetime.now():%Y%m%d_%H%M%S}.sql')

    subprocess.run([
        'mysqldump', 'mydb',
        '-o', backup_file
    ])

    os.chmod(backup_file, 0o600)  # Nur Eigentümer lesen/schreiben

# SICHER: Sichere temporäre Dateibehandlung
def process_upload_safe(file_data):
    # Sichere Temp-Datei verwenden
    with tempfile.NamedTemporaryFile(
        mode='wb',
        dir='/var/tmp/myapp',  # Dediziertes Temp-Verzeichnis
        delete=False,
        prefix='upload_',
        suffix='.tmp'
    ) as f:
        # Berechtigungen vor dem Schreiben setzen
        os.chmod(f.name, 0o600)
        f.write(file_data)
        return f.name

# SICHER: Dateien über Anwendung bereitstellen (kein direkter Zugriff)
@app.route('/download/<file_id>')
@login_required
def download_file(file_id):
    # Dateien außerhalb Web-Root gespeichert
    file_record = File.query.get(file_id)

    if not file_record or file_record.owner_id != current_user.id:
        abort(404)

    # Datei über Anwendung mit Zugriffskontrolle bereitstellen
    return send_from_directory(
        '/var/data/uploads',  # Nicht web-zugänglich!
        file_record.stored_name,
        as_attachment=True,
        download_name=file_record.original_name
    )
// SICHER: Java mit sicherer Dateibehandlung
public class SicheresFileHandling {

    // Logs außerhalb Webverzeichnis
    private static final String LOG_DIR = "/var/log/myapp";
    private static final Path LOG_PATH = Paths.get(LOG_DIR, "app.log");

    static {
        try {
            Files.createDirectories(Paths.get(LOG_DIR));
            // Verzeichnisberechtigungen setzen
            Set<PosixFilePermission> dirPerms = PosixFilePermissions.fromString("rwxr-x---");
            Files.setPosixFilePermissions(Paths.get(LOG_DIR), dirPerms);
        } catch (IOException e) {
            throw new RuntimeException("Logverzeichnis könnte nicht erstellt werden", e);
        }
    }

    public void logOperation(String userId, String action) {
        try {
            // Keine sensiblen Daten protokollieren
            String logEntry = String.format("%s - Benutzer %s: %s%n",
                LocalDateTime.now(), userId, action);

            Files.write(LOG_PATH, logEntry.getBytes(),
                StandardOpenOption.CREATE, StandardOpenOption.APPEND);

            // Dateiberechtigungen setzen
            Set<PosixFilePermission> filePerms = PosixFilePermissions.fromString("rw-r-----");
            Files.setPosixFilePermissions(LOG_PATH, filePerms);

        } catch (IOException e) {
            // Fehler behandeln
        }
    }

    // SICHER: Export an sicheren Speicherort
    public void exportUserData(List<User> users) throws IOException {
        // Export an nicht-web-zugänglichen Speicherort
        Path exportDir = Paths.get("/var/data/exports");
        Files.createDirectories(exportDir);

        String filename = "users_" + System.currentTimeMillis() + ".csv";
        Path exportPath = exportDir.resolve(filename);

        try (PrintWriter pw = new PrintWriter(Files.newBufferedWriter(exportPath))) {
            for (User user : users) {
                // Nur nicht-sensible Felder exportieren
                pw.println(String.format("%s,%s",
                    user.getId(), user.getDisplayName()));
            }
        }

        // Restriktive Berechtigungen
        Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
        Files.setPosixFilePermissions(exportPath, perms);
    }
}
// SICHER: Node.js mit sicherer Dateibehandlung
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

// Logs außerhalb öffentlichem Verzeichnis
const LOG_DIR = '/var/log/myapp';
const LOG_PATH = path.join(LOG_DIR, 'app.log');

// Logverzeichnis mit ordnungsgemäßen Berechtigungen erstellen
if (!fs.existsSync(LOG_DIR)) {
    fs.mkdirSync(LOG_DIR, { recursive: true, mode: 0o750 });
}

function logRequestSafe(req) {
    // Nur nicht-sensible Daten protokollieren
    const logEntry = JSON.stringify({
        timestamp: new Date().toISOString(),
        method: req.method,
        path: req.path,
        ip: req.ip,
        userAgent: req.headers['user-agent']
        // Keine Auth-Tokens, Cookies oder Request-Bodies!
    });

    fs.appendFileSync(LOG_PATH, logEntry + '\n', { mode: 0o640 });
}

// SICHER: Uploads außerhalb öffentlichem Verzeichnis gespeichert
const UPLOAD_DIR = '/var/data/uploads';  // Nicht in public!

app.post('/upload', requireAuth, (req, res) => {
    const file = req.files.document;

    // Zufälligen Dateinamen generieren
    const ext = path.extname(file.name);
    const storedName = crypto.randomUUID() + ext;
    const uploadPath = path.join(UPLOAD_DIR, storedName);

    file.mv(uploadPath, (err) => {
        if (err) return res.status(500).send('Upload fehlgeschlagen');

        // Restriktive Berechtigungen setzen
        fs.chmodSync(uploadPath, 0o600);

        // Metadaten in Datenbank speichern
        FileRecord.create({
            originalName: file.name,
            storedName: storedName,
            ownerId: req.user.id
        });

        res.json({ id: storedName });
    });
});

// SICHER: Dateien über Anwendung bereitstellen
app.get('/download/:fileId', requireAuth, async (req, res) => {
    const file = await FileRecord.findById(req.params.fileId);

    if (!file || file.ownerId !== req.user.id) {
        return res.status(404).send('Datei nicht gefunden');
    }

    const filePath = path.join(UPLOAD_DIR, file.storedName);

    res.download(filePath, file.originalName);
});

// SICHER: Deployment ohne .git-Verzeichnis
// .dockerignore oder Deployment-Skript:
/*
.git
.gitignore
*.log
*.sql
*.bak
.env
node_modules
*/

Ausgenutzt in der Praxis

Git-Verzeichnis-Offenlegung

Zahlreiche Websites haben ihre .git-Verzeichnisse offengelegt, was Angreifern ermöglichte, gesamte Quellcode-Repositories einschließlich Anmeldedaten und Geheimnissen herunterzuladen.

Datenbank-Dump-Offenlegung

SQL-Backup-Dateien, die in web-zugänglichen Verzeichnissen belassen wurden, haben Millionen von Datensätzen einschließlich Benutzeranmeldedaten und persönlicher Daten offengelegt.

Logdatei-Datenverletzungen

Anwendungslogs mit sensiblen Informationen wurden durch Directory-Traversal oder direkten URL-Zugriff abgerufen.


Tools zum Testen/Ausnutzen


CVE-Beispiele

  • CVE-2021-44228 — Log4j (im Zusammenhang mit Log-Injection).

  • CVE-2019-11358 — Offengelegte Konfigurationsdateien.

  • Zahlreiche anwendungsspezifische Dateioffenlegungs-CVEs.


Referenzen

  1. MITRE. "CWE-538: Insertion of Sensitive Information into Externally-Accessible File or Directory." https://cwe.mitre.org/data/definitions/538.html

  2. OWASP. "Sensitive Data Exposure." https://owasp.org/www-project-web-security-testing-guide/