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
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Datenoffenlegung Sensible Daten einschließlich Anmeldedaten, PII und Geschäftsinformationen werden offengelegt. |
| Authentifizierung | Bereich: Anmeldedaten-Diebstahl Offengelegte Konfigurationsdateien offenbaren Datenbank- und API-Anmeldedaten. |
| Integrität | Bereich: 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
-
GitTools — .git-Verzeichnisse extrahieren.
-
DirBuster/GoBuster — Verzeichnis-Enumeration.
-
Google Dorks — Offengelegte Dateien finden.
-
Nuclei — Templates zur Erkennung sensibler Dateien.
CVE-Beispiele
-
CVE-2021-44228 — Log4j (im Zusammenhang mit Log-Injection).
-
CVE-2019-11358 — Offengelegte Konfigurationsdateien.
-
Zahlreiche anwendungsspezifische Dateioffenlegungs-CVEs.
Referenzen
-
MITRE. "CWE-538: Insertion of Sensitive Information into Externally-Accessible File or Directory." https://cwe.mitre.org/data/definitions/538.html
-
OWASP. "Sensitive Data Exposure." https://owasp.org/www-project-web-security-testing-guide/