Zum Inhalt springen

10. Security

Zu Zen-Modus wechseln

Eine Datenbank enthält meist die wertvollsten Daten eines Betriebs: Kunden, Bestellungen, Noten, Zugangsdaten. Sie ist damit ein lohnendes Ziel. Sicherheit ist keine einzelne Einstellung, sondern eine Kette aus mehreren Schichten, die alle halten müssen. Fällt eine, hilft der Rest wenig.

Dieses Kapitel bündelt die Betriebsseite: Wie schützt man ein Datenbanksystem, wie behält man es im Blick, und wie hält man es verfügbar. Vieles davon knüpft an frühere Kapitel an, etwa Benutzer und Rechte, Physische Organisation (Backup) und Datenbank-Schnittstellen.

flowchart TB
  A["Netzwerk<br/>Firewall, TLS, kein offener Port ins Internet"] --> B
  B["Authentifizierung<br/>starke Passwörter, pg_hba.conf, Zertifikate"] --> C
  C["Autorisierung<br/>Rollen, Least Privilege, Ownership"] --> D
  D["Anwendung<br/>parametrisierte Abfragen, Input-Prüfung"] --> E
  E["Daten<br/>Verschlüsselung, Backups, Audit-Log"]

Jede Schicht fängt Fehler der darüberliegenden ab. Ein guter Betrieb setzt an allen an, nicht nur an einer.

Der häufigste schwere Fehler ist eine Datenbank, die offen im Internet hängt. Ein Datenbankserver gehört hinter eine Firewall und sollte nur von den Anwendungsservern erreichbar sein.

  1. Nicht öffentlich binden. In PostgreSQL steuert listen_addresses in postgresql.conf, auf welchen Adressen der Server lauscht. Für lokale Entwicklung reicht localhost.

  2. Firewall-Regeln setzen. Nur die IP-Adressen der App-Server dürfen auf Port 5432 (PostgreSQL) bzw. 3306 (MariaDB/MySQL) zugreifen.

  3. Verbindungen verschlüsseln. TLS schützt Zugangsdaten und Daten auf dem Weg. In produktiven Umgebungen ist mindestens sslmode=require Pflicht.

Wer sich verbindet, muss beweisen, wer er ist. In PostgreSQL regelt die Datei pg_hba.conf, welche Methode für welche Verbindung gilt (Passwort, scram-sha-256, Zertifikat, lokaler Socket).

  • Starke, eindeutige Passwörter je Rolle, nie geteilt.
  • Zugangsdaten liegen in Umgebungsvariablen oder einem Secrets Manager, nie im Quellcode oder in git.
  • Für sehr sensible Umgebungen: Zertifikats- oder Kerberos-basierte Authentifizierung.

Die stärkste Einzelmaßnahme ist, jedem Zugang nur die minimal nötigen Rechte zu geben. Das wurde in Benutzer und Rechte ausführlich behandelt. Für den Betrieb zählt vor allem:

  • Die Anwendung läuft nicht als Superuser, sondern als eigener Rollen-Account mit reinem CRUD-Recht.
  • Reporting- und BI-Zugänge sind reine Nur-Lese-Rollen.
  • Wird ein Zugang kompromittiert, bleibt der Schaden auf dessen Rechte begrenzt.

SQL-Injection entsteht, wenn Benutzereingaben direkt in einen SQL-String verkettet werden. Ein Angreifer kann dann eigene SQL-Fragmente einschleusen.

// DANGEROUS — input goes straight into the SQL
const q = `SELECT * FROM users WHERE name = '${input}'`;
// input = "'; DROP TABLE users; --" destroys the table

Die Gegenmaßnahme ist immer dieselbe: parametrisierte Abfragen (Prepared Statements). Die Datenbank behandelt den Parameter dann als reinen Wert, nie als Code.

// SAFE — the value is passed as a parameter
await pool.query('SELECT * FROM users WHERE name = $1', [input]);

Ein System, das niemand beobachtet, fällt unbemerkt aus oder wird unbemerkt angegriffen. Zum Betrieb gehört, den Zustand sichtbar zu machen.

  • Logs: Fehlgeschlagene Logins, langsame Abfragen und Fehler protokollieren. PostgreSQL schreibt das in sein Server-Log (log_min_duration_statement macht langsame Abfragen sichtbar).
  • Aktive Verbindungen: pg_stat_activity zeigt, wer gerade was tut und ob Verbindungen in idle in transaction hängen.
  • Kennzahlen: Verbindungszahl, Cache-Trefferrate, Sperren (Locks), Plattenauslastung. Werkzeuge wie Grafana visualisieren diese über die Zeit.
-- find the slowest currently running queries
SELECT pid, now() - query_start AS duration, state, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;

Backups wurden in Physische Organisation technisch behandelt. Für den Betrieb zählt die Strategie dahinter:

  • Regelmäßig und automatisch: ein Backup, das man von Hand starten muss, wird vergessen.
  • Getestet: ein Backup, das sich noch nie zurückspielen ließ, ist kein Backup. Restores regelmäßig üben.
  • Getrennt gelagert: Kopien außerhalb des Servers, damit ein Ausfall oder Ransomware nicht Original und Backup zugleich trifft.
  • Mehrere Generationen: täglich, wöchentlich, monatlich, damit auch ein spät entdeckter Fehler noch reparierbar ist.

Eine bewährte Merkregel ist 3-2-1: drei Kopien, auf zwei verschiedenen Medien, eine davon außer Haus.

Damit ein einzelner Serverausfall nicht den Betrieb stoppt, kann man Daten auf mehrere Server spiegeln. Bei der Replikation übernimmt ein primärer Server die Schreibzugriffe und gibt die Änderungen an ein oder mehrere Replikate weiter.

  • Lesenlastverteilung: Replikate können Leseabfragen übernehmen und den Primärserver entlasten.
  • Failover: Fällt der Primärserver aus, kann ein Replikat seine Rolle übernehmen.

Das Thema wird in der 5. Klasse unter Cluster und Replikation vertieft. Für den 4.-Klass-Betrieb genügt das Grundprinzip: ein Server allein ist ein Single Point of Failure.

Sicherheit ist geschichtet. Kein einzelner Handgriff macht ein System sicher, aber mehrere zusammen machen es widerstandsfähig:

  • Datenbank hinter Firewall, Verbindungen per TLS.
  • Starke Authentifizierung, Zugangsdaten außerhalb des Codes.
  • Least Privilege für jede Rolle, Anwendung nie als Superuser.
  • Parametrisierte Abfragen gegen SQL-Injection.
  • Monitoring, damit Probleme sichtbar werden.
  • Getestete, getrennt gelagerte Backups nach 3-2-1.
  • Replikation gegen den Ausfall eines einzelnen Servers.