MCP-DB Sicherheit
Vollständige Sicherheitsmaßnahmen für 10 MCP-DB Tools.
Sicherheitsübersicht
| Maßnahme | Implementierung | Betrifft |
| Prepared Statements | cursor.execute(query, params) | Alle Tools |
| Identifier-Validierung | Regex ^[a-zA-Z0-9_]+$ | INSERT, UPDATE, DELETE |
| Mandatory WHERE | where dict Pflicht | UPDATE, DELETE |
| Default LIMIT 100 | Automatisch bei DELETE | DELETE |
| DDL Statement-Validierung | ExecuteValidator | db_execute |
| Keyword Blocklist (15) | Word Boundaries | db_select |
| Database Allowlist | ki_dev, ki_content | Alle Tools |
| Query Timeout | 30 Sekunden | db_select |
| Row Limit | Max 100 | db_select |
| Query Length | Max 2000 Zeichen | db_select |
| Logging | Jede Operation in mcp_log | Alle Tools |
| Blocking-Hook | PreToolUse für Bash | mysql/mariadb CLI |
Schreib-Operationen Sicherheit
db_insert
| Schutz | Implementierung |
| Identifier-Validierung | Tabellen- und Spaltennamen: ^[a-zA-Z0-9_]+$ |
| Prepared Statements | Werte als Parameter, nie String-Konkatenation |
| DB-Whitelist | Nur ki_dev, ki_content erlaubt |
# Sicher: Prepared Statement
INSERT INTO `tasks` (`title`, `status`) VALUES (%s, %s)
# Mit values: ("Mein Task", "pending")
db_update
UPDATE ohne WHERE ist verboten!
| Schutz | Implementierung |
| Mandatory WHERE | Leeres where dict → status: "denied" |
| Identifier-Validierung | Alle Spalten in data und where |
| Prepared Statements | SET und WHERE Werte als Parameter |
# Erlaubt:
db_update(table="tasks", data={"status": "done"}, where={"id": 42})
# Blockiert (kein WHERE):
db_update(table="tasks", data={"status": "done"}, where={})
# → {"status": "denied", "error": "WHERE clause is required..."}
db_delete
DELETE ohne WHERE ist verboten! Default LIMIT: 100
| Schutz | Implementierung |
| Mandatory WHERE | Leeres where dict → status: "denied" |
| Default LIMIT | Automatisch LIMIT 100 wenn nicht angegeben |
| Identifier-Validierung | Spaltennamen in where |
# Erlaubt:
db_delete(table="mcp_log", where={"status": "error"}, limit=50)
# Blockiert (kein WHERE):
db_delete(table="mcp_log", where={})
# → {"status": "denied", "error": "WHERE clause is required..."}
db_execute (DDL)
| Schutz | Implementierung |
| Statement-Whitelist | ALTER, CREATE, DROP, TRUNCATE |
| ExecuteValidator | Prüft erstes Keyword |
| Logging | Statement-Typ in mcp_log |
# Erlaubt:
db_execute("ALTER TABLE tasks ADD COLUMN priority INT")
db_execute("CREATE INDEX idx_status ON tasks(status)")
db_execute("DROP TABLE temp_data")
db_execute("TRUNCATE TABLE cache")
# Blockiert:
db_execute("SELECT * FROM tasks") # Kein DDL
db_execute("INSERT INTO tasks...") # Kein DDL
Identifier-Validierung
Verhindert SQL-Injection über Tabellen-/Spaltennamen:
def _validate_identifier(name: str) -> bool:
"""Validiert Tabellen-/Spaltennamen gegen SQL-Injection."""
return bool(re.match(r"^[a-zA-Z0-9_]+$", name))
| Eingabe | Valide | Grund |
| tasks | Ja | Alphanumerisch |
| chat_sessions | Ja | Mit Underscore |
| tasks; DROP TABLE-- | Nein | Sonderzeichen |
| tasks` | Nein | Backtick |
Blocking-Hook (PreToolUse)
KRITISCH: Direkte mysql/mariadb-Befehle werden auf System-Ebene blockiert!
Blockierte Muster
# Blockierte Befehle (Beispiele)
mysql -u root -pPassword123 -e "SELECT 1"
mariadb -u user -pSecret
mysql --password=geheim --execute="DROP TABLE users"
mysqldump ki_protokoll > backup.sql
sudo mysql -u root -pPass
# Stattdessen MCP-DB verwenden:
db_select - SELECT-Abfragen
db_insert - Datensätze einfügen
db_update - Datensätze ändern
db_delete - Datensätze löschen
db_execute - DDL (ALTER/CREATE/DROP)
db_describe - Tabellenstruktur
db_databases - Datenbanken auflisten
db_tables - Tabellen auflisten
db_schema - Tabellen-Metadaten
db_stats - MCP-Log Statistiken
Hook-Konfiguration
Pfad: /var/www/.claude/settings.local.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/var/www/tools/ki-protokoll/claude-hook/block_direct_db.py",
"timeout": 5
}
]
}
]
}
}
Sicherheitsschichten (Defense in Depth)
| Schicht | Mechanismus | Schutz vor |
| 1. Claude Code Hook |
block_direct_db.py (Exit Code 2) |
Direkte Bash-Befehle mit mysql/mariadb |
| 2. Identifier-Validierung |
Regex ^[a-zA-Z0-9_]+$ |
SQL-Injection über Namen |
| 3. Mandatory WHERE |
where dict Pflicht |
Versehentliches UPDATE/DELETE aller Zeilen |
| 4. Default LIMIT |
LIMIT 100 bei DELETE |
Massenlöschung |
| 5. DDL-Whitelist |
ExecuteValidator |
Unerlaubte DDL-Statements |
| 6. Prepared Statements |
cursor.execute(sql, params) |
SQL-Injection über Werte |
| 7. DB-Whitelist |
Config.ALLOWED_DATABASES |
Zugriff auf andere Datenbanken |
| 8. Audit-Log |
mcp_log Tabelle |
Nachvollziehbarkeit, Incident Response |
Keyword Blocklist (db_select)
| Kategorie | Keywords | Risiko |
| DML | DELETE, INSERT, UPDATE | Datenmanipulation |
| DDL | DROP, TRUNCATE, ALTER, CREATE, RENAME | Strukturänderung |
| DCL | GRANT, REVOKE | Rechteänderung |
| File-Ops | LOAD_FILE, INTO OUTFILE, INTO DUMPFILE | Dateizugriff |
| DoS | BENCHMARK, SLEEP | Resource Exhaustion |
Incident Response
| Event | Indikator | Maßnahme |
| Hook blockiert Befehl |
PreToolUse:Bash hook error: BLOCKIERT |
MCP-DB Tools verwenden |
| WHERE fehlt |
status='denied', "WHERE clause is required" |
where Parameter hinzufügen |
| Ungültiger Identifier |
status='denied', "Invalid column name" |
Spaltenname prüfen |
| Unerlaubtes DDL |
status='denied', "Statement not allowed" |
Nur ALTER/CREATE/DROP/TRUNCATE |
| SQL Injection Versuch |
status='denied', Keyword in error_message |
Log analysieren |
Credential-Management
.env Datei
# Berechtigungen setzen
chmod 600 /opt/mcp-servers/mcp-db/.env
chown root:root /opt/mcp-servers/mcp-db/.env
Keine Credentials in Claude Config
# Registrierung OHNE Credentials
claude mcp add mcp-db \
--transport stdio \
-- /opt/mcp-servers/mcp-db/venv/bin/python \
/opt/mcp-servers/mcp-db/server.py
# Credentials werden aus .env geladen
Verwandte Kapitel