Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Datenbanksicherheit in der Praxis: PostgreSQL, MySQL und MSSQL absichern - Cybersicherheit und digitaler Schutz
Netzwerk- & Endpoint Security

Datenbanksicherheit in der Praxis: PostgreSQL, MySQL und MSSQL absichern

Datenbanken enthalten die wertvollsten Unternehmensdaten - und sind häufig falsch konfiguriert. Dieser Guide erklärt Datenbank-Härtung für PostgreSQL, MySQL und MSSQL: Least-Privilege-Rollen, Authentifizierung, Audit-Logging, Verschlüsselung, SQL Injection Prevention, Backup-Sicherheit und Datenbank-Firewalls.

Vincent Heinen Vincent Heinen Abteilungsleiter Offensive Services
11 Min. Lesezeit
OSCP+ OSCP OSWP OSWA

TL;DR

Unternehmen sichern ihre wertvollsten

Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 52).

Inhaltsverzeichnis (6 Abschnitte)

Die Datenbank ist das Herzstück der meisten Anwendungen - und oft das schwächste Glied. Standardinstallationen sind auf Komfort optimiert, nicht auf Sicherheit: SA-Account mit leerem Passwort, pg_hba.conf die alle Verbindungen erlaubt, keine Audit-Logs. Ein Angreifer der Datenbankzugang erlangt, hat in der Regel das Spiel gewonnen.

Häufige Datenbank-Schwachstellen

Top-Schwachstellen in Datenbankumgebungen:

1. Standard-Credentials (häufigste Schwachstelle!):
   → MySQL root ohne Passwort (Standard bei vielen Installationen)
   → MSSQL SA-Account mit "sa" als Passwort
   → PostgreSQL postgres-User ohne Passwort-Authentifizierung
   → Oracle SYS/SYSTEM mit Standard-Passwörtern

2. Überprivilegierte Accounts:
   → Anwendungs-User hat DBA-Rechte (braucht nur SELECT/INSERT/UPDATE!)
   → Entwickler-Accounts mit Production-Zugang
   → Shared Accounts: alle Entwickler nutzen denselben DB-User

3. Netzwerk-Exposition:
   → Port 5432 (PostgreSQL), 3306 (MySQL), 1433 (MSSQL) direkt aus Internet
   → Firewall-Regel: ANY → DB-Server → ALLOW
   → Kein VPN/Bastion-Host für DB-Administration

4. Fehlende Verschlüsselung:
   → Keine TLS-Verbindung zwischen App und DB
   → Sensitive Daten unverschlüsselt (Passwörter im Klartext!)
   → Backups unverschlüsselt

5. Kein Audit-Logging:
   → Welcher Nutzer hat wann welche Daten abgefragt? → Unbekannt
   → Datenschutzvorfall: keine forensische Spur möglich
   → DSGVO: Nachweispflicht bei Data Breach kaum erfüllbar

SQL-Injection (OWASP #3):
   → Benutzer-Input direkt in SQL-Query eingebaut
   → Angreifer: admin'-- → überspringt WHERE-Klausel
   → Oder: UNION SELECT password FROM users
   → Lösung: Prepared Statements IMMER, String-Konkatenation NIE

PostgreSQL Härtung

PostgreSQL: Von der Installation zur sicheren Konfiguration

1. pg_hba.conf - Verbindungsauthentifizierung:

Standard (gefährlich!):
  # TYPE  DATABASE  USER    ADDRESS    METHOD
  local   all       all                peer
  host    all       all    127.0.0.1   md5
  host    all       all    ::1/128     md5

Gehärtet:
  # Lokale Verbindungen: nur für PostgreSQL-Admin
  local   all       postgres            peer
  # Alle anderen lokalen: scram-sha-256 (md5 ist schwach!)
  local   all       all                 scram-sha-256
  # Netzwerk: nur aus App-Subnet, spezifische DBs
  host    myapp     appuser  10.0.1.0/24  scram-sha-256
  # Keine Verbindung von außerhalb (kein 0.0.0.0/0!)
  # PostgreSQL 14+: scram-sha-256 ist Standard

2. postgresql.conf Härtung:
  # Nur auf benötigten Interfaces lauschen (nicht 0.0.0.0!)
  listen_addresses = 'localhost,10.0.2.5'  # Nur Loopback + DB-NIC

  # SSL erzwingen:
  ssl = on
  ssl_cert_file = 'server.crt'
  ssl_key_file = 'server.key'
  ssl_min_protocol_version = 'TLSv1.2'

  # Log-Konfiguration für Audit:
  log_connections = on
  log_disconnections = on
  log_duration = off  # nur bei Performance-Debugging einschalten!
  log_min_duration_statement = 1000  # Slow Queries > 1s loggen
  log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h '
  log_statement = 'ddl'  # alle DDL-Statements loggen (CREATE, DROP, ALTER)

3. Least Privilege Rollen:

  -- Anwendungs-User: nur DML, kein DDL, keine pg_catalog-Rechte
  CREATE USER appuser WITH PASSWORD 'StarkesPasswort123!';
  GRANT CONNECT ON DATABASE myapp TO appuser;
  GRANT USAGE ON SCHEMA public TO appuser;
  GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;
  GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO appuser;

  -- Read-Only User für Reporting:
  CREATE USER reporting WITH PASSWORD 'AndersStarkesPasswort!';
  GRANT CONNECT ON DATABASE myapp TO reporting;
  GRANT USAGE ON SCHEMA public TO reporting;
  GRANT SELECT ON ALL TABLES IN SCHEMA public TO reporting;

  -- KEIN Zugang zu postgresql-spezifischen Systemkatalogen:
  REVOKE ALL ON SCHEMA information_schema FROM appuser;

4. Row Level Security (RLS) für Multi-Tenant:
  ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
  CREATE POLICY tenant_isolation ON orders
    USING (tenant_id = current_setting('app.tenant_id')::INTEGER);
  -- App setzt: SET app.tenant_id = '42';
  -- Dann sieht User nur Zeilen mit tenant_id = 42!

5. pgAudit Extension (Enterprise-Audit):
  CREATE EXTENSION pgaudit;
  # postgresql.conf:
  pgaudit.log = 'write, function, role, ddl, misc'
  pgaudit.log_catalog = off  # Kein Log für pg_catalog (zu viel Noise)
  # → Loggt: welcher User hat wann welche Tabellen modifiziert

MySQL / MariaDB Härtung

MySQL: Sicheres Setup

1. mysql_secure_installation ausführen:
  sudo mysql_secure_installation
  → Root-Passwort setzen
  → Anonymous Users entfernen
  → Remote Root-Login deaktivieren
  → Test-Datenbank entfernen

2. Root-Zugang einschränken:
  -- Root nur von localhost (NICHT von außen!)
  ALTER USER 'root'@'localhost' IDENTIFIED BY 'SehrStarkesRootPasswort!';
  DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
  FLUSH PRIVILEGES;

  -- Kein Zugang von '%' (alle Hosts) für Admin-Accounts!
  SELECT User, Host FROM mysql.user;

3. Anwendungs-User:
  -- Anwendungs-User nur mit notwendigen Rechten:
  CREATE USER 'appuser'@'10.0.1.%' IDENTIFIED BY 'AppPasswort!';
  GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'appuser'@'10.0.1.%';
  -- KEIN GRANT OPTION (kann keine weiteren Rechte vergeben!)
  -- KEIN FILE, PROCESS, SUPER (System-Privileges!)
  FLUSH PRIVILEGES;

4. my.cnf Härtung:
  [mysqld]
  # Nur auf localhost lauschen:
  bind-address = 127.0.0.1
  # Oder: spezifische IP: bind-address = 10.0.2.5

  # SSL aktivieren:
  ssl-ca=/etc/mysql/ssl/ca.pem
  ssl-cert=/etc/mysql/ssl/server-cert.pem
  ssl-key=/etc/mysql/ssl/server-key.pem
  require_secure_transport=ON  # Erzwingt SSL für alle Verbindungen!

  # Audit-Logging (Enterprise oder MariaDB Audit Plugin):
  plugin-load-add=server_audit
  server_audit_logging=ON
  server_audit_events=CONNECT,QUERY_DDL,QUERY_DML

  # Binary Log für PITR (Point-in-Time-Recovery):
  log_bin = /var/log/mysql/mysql-bin.log
  expire_logs_days = 7

5. Passwort-Validierung:
  INSTALL PLUGIN validate_password SONAME 'validate_password.so';
  SET GLOBAL validate_password.policy=STRONG;
  SET GLOBAL validate_password.length=12;
  SET GLOBAL validate_password.mixed_case_count=1;
  SET GLOBAL validate_password.number_count=1;
  SET GLOBAL validate_password.special_char_count=1;

Microsoft SQL Server Härtung

MSSQL: Sichere Konfiguration

1. SA-Account deaktivieren:
  -- SA-Account ist Legacy, sollte niemals genutzt werden!
  ALTER LOGIN [sa] DISABLE;
  ALTER LOGIN [sa] WITH NAME = [sa_disabled];  -- Umbenennen als weitere Barriere
  -- Windows Authentication bevorzugen (Active Directory Integration!)

2. Least Privilege für Anwendungen:
  -- Anwendungs-Login anlegen:
  CREATE LOGIN [AppLogin] WITH PASSWORD = 'StarkesPasswort123!',
    CHECK_POLICY = ON,   -- Passwort-Policy erzwingen
    CHECK_EXPIRATION = ON;  -- Passwort-Ablauf

  -- Database User mit minimalem Privilege:
  USE [MyDatabase];
  CREATE USER [AppUser] FOR LOGIN [AppLogin];
  -- Rolle statt direkte Berechtigungen:
  EXEC sp_addrolemember 'db_datareader', 'AppUser';
  EXEC sp_addrolemember 'db_datawriter', 'AppUser';
  -- NICHT: db_owner oder sysadmin!

3. Surface Area Configuration:
  -- Nicht benötigte Features deaktivieren:
  EXEC sp_configure 'xp_cmdshell', 0;         -- KEIN Shell-Zugriff!
  EXEC sp_configure 'remote access', 0;
  EXEC sp_configure 'Ole Automation Procedures', 0;
  EXEC sp_configure 'Ad Hoc Distributed Queries', 0;
  RECONFIGURE;

4. SQL Server Audit:
  -- Audit auf Server-Ebene:
  CREATE SERVER AUDIT [SecurityAudit]
    TO FILE (FILEPATH = N'C:\SQLAudit\')
    WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE);
  ALTER SERVER AUDIT [SecurityAudit] WITH (STATE = ON);

  CREATE SERVER AUDIT SPECIFICATION [ServerAuditSpec]
    FOR SERVER AUDIT [SecurityAudit]
    ADD (SUCCESSFUL_LOGIN_GROUP),
    ADD (FAILED_LOGIN_GROUP),
    ADD (SERVER_ROLE_MEMBER_CHANGE_GROUP),
    ADD (DATABASE_PERMISSION_CHANGE_GROUP)
    WITH (STATE = ON);

5. Transparent Data Encryption (TDE):
  -- Daten-at-Rest verschlüsseln:
  USE master;
  CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MasterKeyPasswort!';
  CREATE CERTIFICATE TDECert WITH SUBJECT = 'TDE Certificate';
  USE MyDatabase;
  CREATE DATABASE ENCRYPTION KEY
    WITH ALGORITHM = AES_256
    ENCRYPTION BY SERVER CERTIFICATE TDECert;
  ALTER DATABASE MyDatabase SET ENCRYPTION ON;
  -- → Alle Datenbankdateien (.mdf, .ndf, .ldf) verschlüsselt
  -- Backup des Master Keys und Zertifikats KRITISCH!

Verschlüsselung und Backup-Sicherheit

Sensitive Daten in der Datenbank schützen:

Column-Level Encryption (für höchste Sensitivität):

PostgreSQL - pgcrypto:
  CREATE EXTENSION pgcrypto;

  -- Verschlüsseltes Speichern:
  INSERT INTO kunden (name, email, iban)
  VALUES (
    'Max Mustermann',
    pgp_sym_encrypt('max@example.de', 'EncryptionKey123!'),
    pgp_sym_encrypt('DE89 3704 0044 0532 0130 00', 'EncryptionKey123!')
  );

  -- Entschlüsseln nur bei Bedarf:
  SELECT name, pgp_sym_decrypt(iban::bytea, 'EncryptionKey123!') AS iban_plain
  FROM kunden WHERE id = 1;

  -- BESSER: Key aus KMS/Vault holen, nicht hardcoded!

Password Hashing (NIEMALS Klartext-Passwörter!):
  -- Argon2id (beste Wahl für neue Systeme):
  -- PHP:   password_hash($password, PASSWORD_ARGON2ID)
  -- Python: argon2-cffi library
  -- Node:  argon2 library

  -- bcrypt (etabliert, weit verbreitet):
  UPDATE users SET password_hash = crypt('passwort', gen_salt('bf', 12))
    WHERE id = 1;
  -- Verifizieren:
  SELECT (password_hash = crypt('eingabe', password_hash)) AS valid FROM users WHERE id=1;

Backup-Sicherheit:

Backup-Verschlüsselung (PostgreSQL mit pg_dump):
  # Verschlüsseltes Backup:
  pg_dump mydb | openssl enc -aes-256-cbc -pbkdf2 -out backup.enc -pass env:BACKUP_KEY

  # Entschlüsseln:
  openssl enc -aes-256-cbc -d -pbkdf2 -in backup.enc -pass env:BACKUP_KEY | psql mydb

  # AWS: RDS-Backups automatisch mit KMS verschlüsselt (wenn aktiviert!)
  aws rds create-db-snapshot --db-instance-identifier mydb \
    --db-snapshot-identifier mydb-backup-2026-03-04
  # KMS Key ID im RDS-Parameter: storage-encrypted=true, kms-key-id=...

Backup-Zugriffskontrolle:
  □ Backups in separatem Storage-Account/Bucket
  □ Cross-Region-Replikation für DR
  □ IAM: nur Backup-Service-Account hat Write-Zugang
  □ Niemand hat Delete-Zugang ohne MFA (S3 Object Lock, Azure Immutable Blob)
  □ Backup-Wiederherstellung testen! (mindestens monatlich)

SQL Injection Prevention

SQL Injection vollständig verhindern:

FALSCH - String-Konkatenation (NIEMALS!):
  # Python (SCHLECHT):
  query = "SELECT * FROM users WHERE username = '" + username + "'"
  cursor.execute(query)
  # Angreifer: username = "admin' OR '1'='1"
  # Query: SELECT * FROM users WHERE username = 'admin' OR '1'='1'
  # → Gibt ALLE User zurück!

RICHTIG - Prepared Statements:
  # Python (SICHER):
  query = "SELECT * FROM users WHERE username = %s"
  cursor.execute(query, (username,))
  # Parameter wird immer als Datenwert behandelt, nie als SQL!

  # Java / JDBC:
  PreparedStatement stmt = conn.prepareStatement(
    "SELECT * FROM users WHERE username = ?"
  );
  stmt.setString(1, username);

  # Node.js / pg:
  const result = await pool.query(
    'SELECT * FROM users WHERE username = $1',
    [username]
  );

  # PHP / PDO:
  $stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username');
  $stmt->execute(['username' => $username]);

ORM verwenden (verhindert SQL Injection by Default):
  # Django ORM (Python):
  User.objects.filter(username=username)  # Automatisch parametrisiert

  # Sequelize (Node.js):
  await User.findOne({ where: { username: username } })

  # Entity Framework (C#):
  context.Users.Where(u => u.Username == username).FirstOrDefault()

  ACHTUNG: Raw Queries in ORMs sind trotzdem gefährlich!
  # Django RAW (gefährlich wenn nicht parametrisiert!):
  User.objects.raw('SELECT * FROM users WHERE username = %s', [username])  # OK
  User.objects.raw(f'SELECT * FROM users WHERE username = {username}')  # FALSCH!

WAF (Web Application Firewall) als zusätzliche Schicht:
  → ModSecurity mit OWASP Core Rule Set (CRS)
  → AWS WAF mit SQL Injection Ruleset
  → Azure Application Gateway mit WAF
  → NICHT als einziger Schutz - Defense in Depth!
  → WAF umgeht man mit Obfuscation: /**/UNION/**/ SELECT...
    → Prepared Statements + WAF = beste Kombination

Stored Procedures (NICHT als SQL-Injection-Schutz!):
  → Stored Procedures können auch SQL Injection enthalten (wenn EXEC(@string))
  → Parametrisierte Stored Procedures: sicher
  → Dynamisches SQL in SP: gefährlich

Datenbanksicherheit ist ein kontinuierlicher Prozess, keine Einmalmaßnahme. Neue Entwickler fügen unsicheren Code hinzu, Konfigurationen ändern sich, neue Systeme werden angebunden. AWARE7 prüft Datenbankimplementierungen als Teil von Applikations-Penetrationstests - von SQL Injection bis zu Berechtigungsanalyse und Audit-Log-Review.

Applikations-Penetrationstest anfragen | Penetrationstest Web-Applikationen

Nächster Schritt

Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel — unverbindlich und kostenlos.

Kostenlos · 30 Minuten · Unverbindlich

Artikel teilen

Über den Autor

Vincent Heinen
Vincent Heinen

Abteilungsleiter Offensive Services

M.Sc. IT-Sicherheit mit über 5 Jahren Erfahrung in offensiver Sicherheitsanalyse. Leitet die Durchführung von Penetrationstests mit Spezialisierung auf Web-Applikationen, Netzwerk-Infrastruktur, Reverse Engineering und Hardware-Sicherheit. Verantwortlich für mehrere Responsible Disclosures.

OSCP+ OSCP OSWP OSWA
Zertifiziert ISO 27001ISO 9001AZAVBSI

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung