Sicherheit

Sicherheit des MCP-Servers für Craft CMS: Die Grenze ist ein Craft-Benutzer.

Ein KI-Client, der eine laufende Site ändern kann, braucht klare Grenzen. Diese Seite nennt jede Grenze in der Reihenfolge, in der ein Aufruf sie trifft. Sie sagt auch, was jede Grenze nicht abdeckt.

Was jeder Aufruf durchläuft

In dieser Reihenfolge. Ein Aufruf, der an einem Schritt scheitert, erreicht das Werkzeug nie.

  1. Aufrufgrenze — Aufrufe je Minute, gezählt je Token. Eine Bremse gegen ein Modell in der Schleife, kein Schutz gegen einen entschlossenen Aufrufer mit gültigem Token.
  2. Token-Scope — read, content oder full. Der Scope verengt nur, was der zugeordnete Benutzer ohnehin darf.
  3. Edition — das Plugin erscheint in einer einzigen Edition, dieser Schritt geht heute also immer durch. Es gibt ihn, damit eine spätere Aufteilung in Editionen kein Werkzeug still herausgeben kann.
  4. Nur-Lesen-Modus — solange er an ist, läuft nichts, was etwas verändert, was auch immer die Einstellungen darunter sagen.
  5. Freischaltung — jedes Werkzeug, das etwas verändert, ist aus, bis ein Administrator es einzeln einschaltet.
  6. Administrator-Prüfung — die Administrator-Werkzeuge verlangen zusätzlich, dass der zugeordnete Benutzer Administrator ist.
  7. Argument-Schema — Argumente werden gegen das Schema des Werkzeugs geprüft, bevor das Werkzeug sie sieht.
  8. Crafts eigene Rechteprüfung, je Element — dann fragt das Werkzeug Craft für dieses Element und diese Site, ob der zugeordnete Benutzer es sehen, speichern oder löschen darf.

Zwei Arten von Absage, mit Absicht. Ein Werkzeug, das es nicht gibt, und eines, das der Scope des Tokens nicht umfasst, werden gleich beantwortet — Unknown tool —, weil der Unterschied einem Angreifer verriete, was es sonst noch gibt. Ein abgeschaltetes Werkzeug, eines im Nur-Lesen-Modus und eines, das Administratoren vorbehalten ist, steht ebenfalls nicht in der Liste; wird es direkt aufgerufen, nennt die Antwort den Grund im Klartext, damit der Betrieb den Fehler nicht bei sich sucht.

Warum der Benutzer die eigentliche Grenze ist

Jeder Token gehört zu genau einem Craft-Benutzer. Gefragt wird zweimal, und die zweite Frage ist die entscheidende: zuerst die grobe, ob diese Art von Handlung diesem Benutzer überhaupt offensteht, und dann die genaue — darf dieser Benutzer das mit diesem Element auf dieser Site. Ein Redakteur, der nur auf einer Site und in einer Section arbeiten darf, erreicht über den KI-Client genau das und sonst nichts.

Das hat eine Folge, die man aussprechen sollte: Ein Token, der einem Administrator gehört, kann, was ein Administrator kann. Geben Sie einem Token den kleinsten Benutzer, der die Aufgabe noch erfüllt, und verengen Sie mit dem Scope weiter.

Eine unbekannte oder gesperrte Section oder Kategoriegruppe liefert eine leere Liste, keinen Fehler — ein Fehler würde bestätigen, dass es die Sache gibt. Ein Site-Handle, den es nicht gibt, wird dagegen als Fehler gemeldet: Ein falsches Handle ist ein Fehler im Aufruf, keine Grenze.

Token

  • Der Klartext wird genau einmal gezeigt, beim Anlegen. Gespeichert wird nur ein SHA-256-Hash, er lässt sich also nicht erneut anzeigen.
  • Token liegen in einer eigenen Tabelle, nie in der Project Config — die in Git eingecheckt wird.
  • Die Token-Seite im Control Panel steht nur Administratoren offen. Craft legt für jedes Plugin mit Seitenleisten-Eintrag eine Berechtigung «Access AlphaBridge MCP» an; sie zu vergeben öffnet diese Seite Nicht-Administratoren nicht.
  • Ein Token kann ein Ablaufdatum tragen und lässt sich jederzeit widerrufen; der nächste Aufruf damit scheitert.
  • Der Authorization-Header wird in Crafts eigenen Request-Kontext-Protokolleinträgen geschwärzt, ein Token landet also nicht in storage/logs, wenn eine Anfrage protokolliert wird.

Das Prüfprotokoll, und was es bewusst weglässt

Jeder Werkzeugaufruf wird festgehalten: wann, welches Werkzeug, welcher Craft-Benutzer und der Ausgang — erlaubt, abgewiesen oder fehlgeschlagen. Auch abgewiesene Aufrufe stehen drin; sie sind die interessanten.

Argumentnamen werden festgehalten, Argumentwerte nicht. Sie sehen, dass ein Eintrag mit einer Section und einem Titel angelegt wurde; den Titel können Sie aus dem Protokoll nicht lesen. Das ist Absicht: Ein Protokoll mit Werten wäre eine zweite, schlechter geschützte Kopie Ihrer Inhalte.

Das Protokoll zu lesen verlangt den Administrator-Scope. Es wird auf eine feste Grösse gekürzt und kann so nicht unbegrenzt wachsen.

Eine ehrliche Grenze: Den Protokolleintrag zu schreiben ist ein Bemühen, keine Garantie. Scheitert dieses Schreiben selbst — etwa an einem Datenbankfehler —, wird der Aufruf nicht abgebrochen; der Fehlschlag geht stattdessen in Crafts eigenes Protokoll. Nehmen Sie das Prüfprotokoll deshalb als sehr gute Aufzeichnung, nicht als Zusicherung, dass nichts fehlen kann.

Crafts Protokolle zu lesen ist enger, als es aussieht

Das Protokoll-Werkzeug gibt Crafts Einträge ohne den Request-Kontext zurück, den Craft daran hängt. Dieser Kontext enthält $_POST, $_COOKIE und $_SESSION — bei einem fehlgeschlagenen Login also ein Passwort im Klartext. SQL-Anweisungen und Stapelspuren werden an der ersten Marke abgeschnitten.

Deshalb ist das Protokoll-Werkzeug ein Administrator-Werkzeug und standardmässig aus.

Die Datenbank-Werkzeuge und ihre Grenzen

Zwei Werkzeuge erreichen die Datenbank, und nur eines davon führt SQL aus, das Sie schreiben. craft_db_schema listet Tabellennamen und kennzeichnet die geschützten als nicht lesbar. craft_db_query führt ein SELECT auf eine Tabelle aus und weist ab, bevor irgendetwas die Datenbank erreicht:

  • Joins, Unterabfragen, Common Table Expressions und UNION
  • jede Funktion ausser count, min, max, avg und sum
  • geschützte Tabellen — Benutzer, Sitzungen, Token, die Project Config und weitere, die Zugangsdaten oder Geheimnisse halten
  • Ausdrücke, die das Serverkonto oder eine Sequenz nennen

Eine Schranke aus Regeln kennt nur, was man ihr gesagt hat. Deshalb sind beide Administrator-Werkzeuge, deshalb ist das abfragende standardmässig aus, und deshalb schreibt keines von beiden je.

Origins und DNS-Rebinding

Trägt eine Anfrage einen Origin-Header, muss dieser auf der Positivliste stehen, sonst antwortet der Endpunkt mit 403. Die Liste ist das, was Sie konfiguriert haben, plus die Site-URL — aber nur, soweit sich diese statisch bestimmen lässt. Eine Site-URL aus Crafts @web-Alias zählt nicht: Craft leitet @web aus dem Host-Header der eingehenden Anfrage ab, ein Angreifer würde also mit einem Wert verglichen, den er selbst gewählt hat.

In einer solchen Installation ist die konfigurierte Liste die einzige Quelle, und ist sie leer, wird jede Anfrage mit Origin abgewiesen. Server-zu-Server-Clients senden keinen Origin und sind nicht betroffen.

Was dieses Plugin nicht tut

Jeder dieser Punkte ist eine Entscheidung, und jeder nimmt eine Risikoklasse ganz weg, statt sie zu verwalten.

  • Kein Ausführen beliebigen Codes. Kein Werkzeug führt PHP, Shell-Befehle oder ein übergebenes Template aus.
  • Kein Deployment und keine Dateiübertragung. Das Plugin bewegt keine Dateien und fasst nie einen Server über SSH oder FTP an.
  • Kein Plugin-Lebenszyklus. Nichts installiert, aktiviert, deaktiviert oder entfernt Plugins.
  • Kein Schreiben in Project Config oder Benutzer. Craft setzt die Regeln dafür in seinen Controllern durch; dieses Plugin spricht mit der Element-API, die diese Regeln nicht sieht — also schreibt es dort gar nicht.
  • Keine Tags. Craft kennt für Tags keine Berechtigung, jede Schranke müsste erfunden statt durchgesetzt werden.
  • Keine Feldeinstellungs-Werte. Feldeinstellungen kommen nur als Namen zurück, nie als Werte, weil ein Plugin dort alles hineinlegen kann. Das ist kein pauschales Versprechen über Geheimnisse: Das Datenbank-Werkzeug erreicht jede physische Tabelle, deren Name nicht auf seiner Sperrliste steht, und eine fremde Plugin-Tabelle mit unauffälligem Namen steht dort nicht. Genau darum ist dieses Werkzeug Administratoren vorbehalten und standardmässig aus.

Wie das wahr gehalten wird

Jede Änderung durchläuft eine Unit-Suite, statische Analyse auf PHPStan-Stufe 6 ohne Baseline und einen Ende-zu-Ende-Lauf gegen eine frisch gebaute Craft-Installation auf PostgreSQL 16 und MySQL 8.4. Dieser Lauf legt einen Redakteur ohne Administratorrechte an, der genau eine Section sehen und auf genau einer Site arbeiten darf, und weist nach, dass der Rechtefilter wirklich schneidet — nicht bloss, dass die Werkzeuge etwas zurückgeben.

Etwas gefunden? Bitte vertraulich an mail@cultureclub.dev melden, nicht in einem öffentlichen Issue.