Lektion 5 von 6·10 Min Lesezeit

Sicherheit & Best Practices

Ein MCP Server, der einem AI-Modell Tools bereitstellt, hat echte Handlungsmacht. Ohne Sicherheitsmaßnahmen kann ein kompromittierter Server Daten leaken, unautorisierte Aktionen ausführen oder als Angriffsvektor dienen.

Authentication

Token-basierte Authentication

server.tool(
  "get_customer",
  "Kundendaten abrufen",
  { customerId: z.string() },
  async ({ customerId }, context) => {
    // Auth-Token aus dem Context prüfen
    const token = context.meta?.authToken;
    if (!token || !await verifyToken(token)) {
      return {
        content: [{ type: "text", text: "Fehler: Nicht autorisiert" }],
        isError: true
      };
    }
    const customer = await db.getCustomer(customerId);
    return { content: [{ type: "text", text: JSON.stringify(customer) }] };
  }
);

API-Key-Management

  • API-Keys niemals im Code speichern
  • Environment Variables oder Secret Manager nutzen
  • Keys regelmäßig rotieren
  • Separate Keys für Development und Production

Authorization

Role-Based Access Control (RBAC)

const permissions: Record<string, string[]> = {
  "read-only":  ["get_customer", "list_orders", "search_products"],
  "editor":     ["get_customer", "list_orders", "update_order", "create_ticket"],
  "admin":      ["*"]  // Alle Tools
};

function checkPermission(role: string, toolName: string): boolean {
  const allowed = permissions[role];
  return allowed?.includes("*") || allowed?.includes(toolName) || false;
}

Granulare Berechtigungen

  • Lese-Tools: Standardmäßig erlaubt
  • Schreib-Tools: Explizite Freigabe erforderlich
  • Lösch-Tools: Human-Approval erforderlich
  • Admin-Tools: Nur für privilegierte Nutzer

Input Validation

Jeder Tool-Input muss validiert werden, bevor die Aktion ausgeführt wird:

server.tool(
  "execute_query",
  "SQL-Abfrage auf der Datenbank ausführen",
  {
    query: z.string()
      .max(1000)
      .refine(q => !q.toLowerCase().includes("drop"), "DROP nicht erlaubt")
      .refine(q => !q.toLowerCase().includes("delete"), "DELETE nicht erlaubt")
      .refine(q => q.toLowerCase().startsWith("select"), "Nur SELECT-Queries erlaubt")
  },
  async ({ query }) => {
    const result = await db.query(query);
    return { content: [{ type: "text", text: JSON.stringify(result) }] };
  }
);

Sandboxing

Prozess-Isolation

  • MCP Server in separaten Prozessen ausführen
  • Container-basierte Isolation für kritische Server
  • Netzwerk-Zugriff auf das Nötigste beschränken

Ressourcen-Limits

const serverConfig = {
  maxRequestsPerMinute: 60,
  maxTokensPerRequest: 10000,
  timeoutMs: 30000,
  maxConcurrentRequests: 5
};

Rate Limiting

import { RateLimiter } from "./rate-limiter";

const limiter = new RateLimiter({ maxRequests: 60, windowMs: 60000 });

server.tool("search", "Suche durchführen", { query: z.string() },
  async ({ query }) => {
    if (!limiter.allow()) {
      return {
        content: [{ type: "text", text: "Rate Limit erreicht. Bitte warten." }],
        isError: true
      };
    }
    return { content: [{ type: "text", text: await search(query) }] };
  }
);

Audit Logging

Jede Aktion muss protokolliert werden:

async function auditLog(event: {
  tool: string;
  input: unknown;
  output: unknown;
  userId: string;
  timestamp: Date;
  success: boolean;
}) {
  await db.insert("audit_log", event);
  if (!event.success) {
    await alerting.notify(`Tool-Fehler: ${event.tool}`, event);
  }
}

Praxis-Tipp: Security ist kein nachträglicher Gedanke — es wird von Anfang an mitgebaut. Jeder MCP Server sollte mindestens Authentication, Input Validation und Audit Logging haben. Für Production kommen Rate Limiting und Sandboxing dazu.