Become a member!

MCP Firebird 0.6.0: wie du einer Firebird-Anwendung NICHT die Rechte gibst

🌐
Dieser Artikel ist auch in anderen Sprachen verfügbar:
🇬🇧 English  •  🇮🇹 Italiano  •  🇪🇸 Español  •  🇫🇷 Français  •  🇧🇷 Português

Viele Firebird-Anwendungen verbinden sich als SYSDBA, oder jedenfalls mit einem Benutzer, der viel mehr Privilegien hat, als er braucht. Die 0.6.0 von MCP Firebird hilft dir, damit aufzuhören: sie fragt dich, was diese Anwendung tut, und schreibt dir die GRANTs. So kommst du schrittweise zu minimalen Rechten, und du weißt jedes Mal, was du gerade entziehst.

Logo von MCP Firebird, dem MCP-Server, mit dem KI-Assistenten Firebird-Datenbanken diagnostizieren können.

Wo liegt das Problem?

Zu viele Firebird-Branchenanwendungen in Produktion, kleine wie mittlere, verbinden sich als SYSDBA. Und die, die es nicht tun, laufen sehr oft mit einem Benutzer, der viel mehr Privilegien hat, als er braucht und haben sollte. Wer das so geschrieben hat, war nicht faul. Frag mal den, der die Anwendung seit fünfzehn Jahren pflegt, welche Rechte sie eigentlich braucht: die ehrliche Antwort ist „weiß ich nicht, kommt drauf an, was das Modul macht, das Marco 2014 geschrieben hat“.

Ab und zu versucht ein „furchtloser Entwickler“, die Sache in Ordnung zu bringen. Dann aber kommen die Fragen, die dich kurz vor dem ersten REVOKE aufhalten.

  • Und wenn danach irgendwas nicht mehr funktioniert?
  • Und wenn der Kunde um zwei Uhr nachts anruft, weil er keinen Lieferschein mehr abschließen kann?
  • Und wenn der Job, der freitagabends läuft, still und leise stehen bleibt, weil ich vergessen habe, ihm die Privilegien auf genau diese eine Tabelle zu geben?

Das sind vernünftige Fragen. Die Rechnung für einen Fehler zahlst du, und du bist auch der, der ans Telefon geht. Also bleibst du bei SYSDBA, der einzigen Konfiguration, bei der sich irgendwann mal jemand sicher war.

Der Haken ist, was SYSDBA bedeutet. Es ist kein Benutzer mit vielen Rechten: es ist ein Benutzer, bei dem die Rechte gar nicht erst geprüft werden. Für den Eigentümer eines Objekts gilt dasselbe, jedenfalls bei seinen eigenen Objekten. Solange sich die Anwendung so verbindet, ändern die GRANT und REVOKE, die du für sie schreibst, überhaupt nichts.

Der Weg, den ich am häufigsten sehe, ist aber ein anderer: man legt eigens einen Benutzer an, um SYSDBA zu vermeiden, und gibt ihm dann praktisch alle Privilegien. In einer Checkliste geht diese Zeile durch, und der Benutzer kommt trotzdem fast überall dorthin, wo er vorher schon hinkam. Wenigstens werden die Rechte dort geprüft, also kommst du schrittweise zu minimalen Rechten. Aber um etwas zu entziehen, musst du wissen, was die Anwendung wirklich braucht, und damit sind wir wieder bei der Frage von vorhin.

Die Version 0.6.0 von mcp-firebird beantwortet genau diese drei Fragen. Es gibt keinen Knopf „Datenbank absichern“: sie fragt dich nach dem, was die Datenbank ihr nicht verrät, schreibt das SQL aus dem Katalog und sagt dir dann, was dieser Benutzer trotzdem noch erreicht.

Warum das Projekt ankommt

MCP-Server, die sich an eine Datenbank hängen, gibt es einige, und fast alle machen denselben Job: sie lassen dich Abfragen ausführen. mcp-firebird macht etwas anderes, und auf Firebird macht das sonst keiner: es sagt dir, warum die Datenbank langsam ist und was du ausführen musst, um das zu beheben.

Das sind Fragen mit einer genauen Antwort, und die Antwort steckt schon in der Datenbank. Der Index auf der Spalte, nach der du filterst, ist da, aber er ist INACTIVE. Dieser vollständige Scan ist der richtige Plan, weil die Tabelle 89% der Zeilen zurückgibt, die sie liest. Diese Fragen musst du stellen, und die Antworten liest du nur richtig, wenn du weißt, welche Engine du vor dir hast.

Wenn du es noch nicht kennst: der Vorstellungsartikel erklärt, was es ist und wie du es installierst. Eine Delphi-Exe, die dein Assistent selbst startet, über stdin/stdout, ohne Dienste und ohne offene Ports.

Kurz gefasst

  • fb_query: führt ein SELECT rein lesend aus und gibt die Zeilen zurück, mit zwei Obergrenzen statt einer.
  • fb_audit_security: alle Rechte der Datenbank, plus die drei Fälle, die es normalerweise nicht geben sollte; mit user_name sagt es, was ein einzelner Benutzer erreicht und auf welchem Weg.
  • fb_suggest_grants und app_user_plan: die Privilegien eines Anwendungsbenutzers, festgelegt per Interview und geschrieben aus dem Katalog.
  • Eine Rolle ist kein Zugriff: auf jeder Version ist eine Rolle wirkungslos, wenn die Verbindung sie nicht nennt; ab 4.0 macht GRANT DEFAULT das automatisch.
  • Sechzehn Tools kostenlos, rein lesend, von Firebird 2.5 bis 5.0. github.com/danieleteti/mcp-firebird

Die Antworten, die jetzt kommen, sind echte Sessions, ausgeführt, während ich den Artikel geschrieben habe: die Tools auf der Testdatenbank des Projekts unter Firebird 5.0.4, der Teil über die Rollen auf allen vier Engines. Ich gebe sie übersetzt wieder, weil du sie genau so liest: der Server antwortet dem Assistenten auf Englisch, und der erzählt sie dir dann in deiner Sprache. Auf Englisch bleiben die Meldungen, die aus Firebird herauskommen.

Die Zeilen einer Abfrage

Bis zur 0.5.0 gab kein Tool Zeilen zurück: jedes führte eine Abfrage aus, um etwas über sie zu sagen, und warf das Ergebnis dann weg. Du hast diese Unterhaltung aber angefangen, weil eine Abfrage langsam war. Der Rat kommt, du legst den Index an, der Plan ändert sich. Und dann? Ein Plan ist die Ansage, wie die Engine arbeiten will, nicht die Zeit, die sie dafür braucht.

Antwort des MCP-Serversfb_querymax_rows=3
CUSTOMER_ID NAME CITY
0 CUST_0 Rome
4 CUST_4 Rome
8 CUST_8 Rome

3 Zeilen in 18 ms, bei der Obergrenze von 3 gestoppt. Die Abfrage hat noch mehr zu geben: schränke sie mit einem WHERE ein, aggregiere sie, oder erhöhe max_rows (bis 1000).


Was diese Prüfung nicht ausschließt: was diese Zeilen kosten. Sie sind das, was die Abfrage jetzt gerade zurückgibt, über einen Read-only-Snapshot; nichts hier sagt, wie teuer die Antwort war (das sagt fb_analyze_query), und eine abgeschnittene Antwort ist eine Stichprobe des Ergebnisses, nicht das Ergebnis.

Es sind zwei Obergrenzen. max_rows (Default 100, Maximum 1000) wird umgesetzt, indem gar nicht weiter gelesen wird. Ein SELECT * auf zwanzig Millionen Zeilen kostet also nur das Paket Zeilen, bei dem ich es gestoppt habe, nicht die Tabelle. Die zweite zählt Zeichen, rund zwanzigtausend, weil eine Grenze für die Zeilen die Breite nicht begrenzt.

Was kein einfaches SELECT ist, wird schon vor der Engine abgelehnt. Die eigentliche Garantie ist aber eine andere: das Nur-Lesen sitzt auf der Verbindung,

FConn.TxOptions.ReadOnly := AConfig.ReadOnly;   // Firebird.Connection.pas

und dieser Wert kommt nicht aus der .env: er ist im Code fixiert, Result.ReadOnly := True. Jede Transaktion, die der Server öffnet, startet mit dem Read-only-TPB von Firebird.

Ich habe versucht, das zu umgehen. Eine selektierbare Stored Procedure, die intern ein INSERT macht, aufgerufen per SELECT: so etwas winkt eine Textprüfung durch, ohne mit der Wimper zu zucken. Der erste Versuch, ein UPDATE im Klartext, wird gestoppt, bevor er losläuft. Der zweite kommt bei der Engine an, und dort ist Schluss.

Antwort des MCP-Serversfb_querysql=UPDATE AUDIT_LOG SET TXT = 'x'

Nicht ausgeführt: das ist kein einfaches SELECT. Dieser Server liest, er schreibt nicht, und die Transaktionen, die er öffnet, tragen das Read-only-TPB, also würde die Engine das Statement auch dann ablehnen, wenn diese Prüfung es durchlassen würde.


Was diese Prüfung nicht ausschließt: was diese Zeilen kosten. Sie sind das, was die Abfrage jetzt gerade zurückgibt, über einen Read-only-Snapshot; nichts hier sagt, wie teuer die Antwort war (das sagt fb_analyze_query), und eine abgeschnittene Antwort ist eine Stichprobe des Ergebnisses, nicht das Ergebnis.

Antwort des MCP-Serversfb_querysql=SELECT * FROM SP_WRITES
Firebird error (EIBNativeException): [FireDAC][Phys][FB]attempted update during read-only transaction At procedure ‘SP_WRITES’ line: 3, col: 3

Die Tabelle hat danach immer noch null Zeilen. Die Textprüfung umgehst du mit ein bisschen Fantasie, das TPB nicht: eine Blocklist von Schlüsselwörtern weiß nicht, dass EXECUTE PROCEDURE schreiben kann, und auch nicht, was diese Prozedur tut.

Wer erreicht was

fb_audit_security ohne Parameter listet die Rechte der Datenbank auf, eine Zeile pro Empfänger und Objekt. Das hier ist die Testdatenbank des Projekts, aus der ich ein paar Zeilen entfernt habe, um nicht zweimal dasselbe zu zeigen:

Antwort des MCP-Serversfb_audit_security

7 Rechte auf Benutzerobjekten. Die Eigentümer sind nicht aufgelistet: der Eigentümer eines Objekts hat per Definition alles auf diesem Objekt.

Empfänger Objekt Privilegien Grant option Erteilt von
CHK40 R_CHK40 (Rolle) MITGLIED VON nein SYSDBA
PUBLIC CUSTOMERS SELECT nein SYSDBA
PUBLIC NOPK_LOG INSERT, UPDATE nein SYSDBA
REPORT_ROLE (Rolle) BRANCHES SELECT nein SYSDBA
REPORT_USER ORDERS SELECT ja SYSDBA
REPORT_USER REPORT_ROLE (Rolle) MITGLIED VON nein SYSDBA
R_CHK40 (Rolle) TBL_ORDERS SELECT nein SYSDBA

3 benutzerdefinierte Rollen.

  • DEAD_ROLE (Eigentümer SYSDBA), keine Mitglieder
  • REPORT_ROLE (Eigentümer SYSDBA), REPORT_USER
  • R_CHK40 (Eigentümer SYSDBA), CHK40

warning

Befund: PUBLIC hat SELECT auf CUSTOMERS. Das heißt: jeder Benutzer, der sich mit dieser Datenbank verbinden kann, auch die, die nach diesem Recht angelegt wurden.

REVOKE SELECT ON CUSTOMERS FROM PUBLIC;

critical

Befund: PUBLIC hat INSERT, UPDATE auf NOPK_LOG. Das heißt: jeder Benutzer, der sich mit dieser Datenbank verbinden kann, auch die, die nach diesem Recht angelegt wurden. Und hier geht es um das Ändern von Daten.

REVOKE INSERT, UPDATE ON NOPK_LOG FROM PUBLIC;

PUBLIC ist keine Gruppe, in die sich jemand eingetragen hat: es ist jeder, der sich verbinden kann, einschließlich der Benutzer, die angelegt wurden, nachdem das Recht erteilt wurde. Beim Lesen ist es warning, beim Schreiben critical: beim Lesen sieht jemand Daten, die er nicht sehen sollte, beim Schreiben ändert er sie dir, und du weißt nicht, wer es war. Die beiden anderen Befunde sind WITH GRANT OPTION, also wer die Zugriffe erweitern kann, ohne dass ein DBA es merkt, und die Rollen ohne Mitglieder.

Die andere Seite der Frage brauchst du öfter. Nicht „was erteile ich“, sondern „was kann der Benutzer, den ich schon habe“. Die Antwort liegt über drei Stellen verstreut: die direkten Grants, die an PUBLIC und die an die Rollen. Mit user_name:

Antwort des MCP-Serversfb_audit_securityuser_name=REPORT_USER

Was REPORT_USER erreicht.

Objekt Privilegien Auf welchem Weg
CUSTOMERS SELECT PUBLIC
NOPK_LOG INSERT, UPDATE PUBLIC
BRANCHES SELECT Rolle REPORT_ROLE (nur wenn sie aktiv ist)
ORDERS SELECT direkt

Eine mit (nur wenn sie aktiv ist) markierte Zeile läuft über eine Rolle, und in Firebird ist eine Rolle inaktiv, wenn die Verbindung sie nicht nennt, oder wenn sie diesem Benutzer nicht als DEFAULT-Rolle erteilt wurde (ab 4.0). Welcher der beiden Fälle gilt, steht in der Security-Datenbank, mit der sich dieser Server nicht verbindet. Von hier aus lässt sich das also nicht lesen.

Mitglied von: REPORT_ROLE

Schema der drei Wege, auf denen ein Firebird-Benutzer zu einer Tabelle kommt: direkter Grant, an PUBLIC erteiltes Recht, Rolle, die nur aktiv ist, wenn die Verbindung sie nennt. SYSDBA und der Eigentümer des Objekts gehen über keinen der drei Wege.
Die Antwort auf „was erreicht dieser Benutzer“ verteilt sich auf drei Wege, nicht auf einen.

Die Rechte der Anwendung

app_user_plan ist der Prompt, der die Fragen stellt. Vorher schaut er aber selbst nach: Version der Engine, Namen der Objekte und Rechte von PUBLIC, und dann fragt er nur das, was der Katalog ihm nicht sagen kann.

Schema des Prompts app_user_plan: zuerst schaut er Version der Engine, Namen der Objekte und Rechte von PUBLIC an, dann stellt er acht Fragen, dann schreibt er CREATE USER, Rolle und GRANT aus dem Katalog, zuletzt prüft er, was der Benutzer außerhalb des Plans erreicht. Bleiben die Antworten aus, schlägt er den Rückfallplan vor.
Die Fragen kommen nach dem Lesen des Katalogs, nicht davor: er fragt nur, was die Datenbank ihm nicht sagen kann.

In den MCP-Clients taucht ein Prompt als auswählbarer Befehl auf: du „führst ihn nicht aus“, du wählst ihn aus und redest dann. Das läuft ungefähr so.

Du: Ich muss diese Anwendung von SYSDBA wegbekommen.

Der Assistent: Erst schaue ich nach. (ruft fb_info, fb_list_tables, fb_audit_security auf) Die Engine ist 3.0.14, und das macht einen Unterschied, weil unter 4.0 eine Rolle wirkungslos ist, wenn die Verbindung sie nicht nennt. Und solange sich die Anwendung als SYSDBA verbindet, ändert kein GRANT etwas an dem, was sie darf. In den Namen sehe ich zwei Gruppen, 42 Tabellen TBL_* und 7 LKP_*: gehören beide zur Anwendung, und auf welche schreibt sie?

Du: Beide. Auf die TBL_ schreibt sie, die LKP_ liest sie nur.

Der Assistent: Noch zwei. Legt sie Objekte an oder ändert sie welche, während sie läuft, etwa Migrationen beim Start? Und verbindet sich sonst noch etwas mit demselben Benutzer?

Du: Kein DDL. Nachts läuft aber ein Export, der denselben Benutzer verwendet.

Der Assistent: Dann bekommt der Export seinen eigenen Benutzer und seine eigene Rolle, sonst sind die Privilegien der Anwendung am Ende die Summe aus beiden Aufgaben. Ich erzeuge dir den Plan mit fb_suggest_grants, zwei Aufrufe mit demselben role_name: TBL_* schreibend, LKP_* lesend.

Insgesamt sind es acht Fragen, und die, auf die der Katalog schon geantwortet hat, stellt er dir nicht. Diese Unterhaltung ist ein Beispiel, keine aufgezeichnete Session.

Bleiben die Antworten aus, schlägt er den Rückfallplan vor: ein Benutzer mit SELECT, INSERT, UPDATE und DELETE auf allen Benutzertabellen. Das sind keine minimalen Rechte, er kann aber kein DROP machen, das Schema nicht ändern, die Security-Datenbank nicht lesen, keine Benutzer anlegen und den Server nicht stoppen.

Was er nicht macht, und zwar mit Absicht: dir sagen, dass die Anwendung weiter funktionieren wird. Im Prompt steht, dass er das nie behaupten darf, weil er es nicht wissen kann. Unter der Regel liegen echte Fälle: dieser Rückfallplan gibt DML auf den Tabellen und nicht EXECUTE auf den Stored Procedures. Eine Anwendung, die eine davon aufruft, bleibt also stehen.

Dann ist fb_suggest_grants dran. „Der Benutzer APP darf nur auf die Tabellen tbl_* zugreifen“ wird auf Firebird 5.0 zu diesem hier:

Antwort des MCP-Serversfb_suggest_grantsuser_name=APP, object_pattern=tbl_*

Lesezugriff für APP, über die Rolle APP_ROLE.

-- Nur wenn APP noch nicht existiert (dieser Server sieht die Liste der Benutzer nicht):
CREATE USER APP PASSWORD 'change-this-before-running';
CREATE ROLE APP_ROLE;
GRANT SELECT ON TBL_ITEMS TO APP_ROLE;
GRANT SELECT ON TBL_ORDERS TO APP_ROLE;
GRANT EXECUTE ON PROCEDURE TBL_SP_COUNT TO APP_ROLE;
GRANT SELECT ON TBL_V_ORDERS TO APP_ROLE;
GRANT DEFAULT APP_ROLE TO USER APP;
  • 2 Tabellen, 1 View und 1 Prozedur passen zu TBL_%.
  • APP_ROLE wird als DEFAULT-Rolle erteilt (5.0.4), sie ist also auf jeder Verbindung aktiv, die APP öffnet, ohne dass die Anwendung danach fragen muss.
  • Das Passwort hier oben ist ein Platzhalter, kein Vorschlag.

Hält der Plan?

warning

Befund: APP wird auch CUSTOMERS erreichen, das außerhalb dieses Plans liegt: PUBLIC hat SELECT auf diesem Objekt, und APP ist Mitglied von PUBLIC wie jeder andere Benutzer.

REVOKE SELECT ON CUSTOMERS FROM PUBLIC;

critical

Befund: APP wird auch NOPK_LOG erreichen, das außerhalb dieses Plans liegt: PUBLIC hat INSERT, UPDATE auf diesem Objekt, und APP ist Mitglied von PUBLIC wie jeder andere Benutzer.

REVOKE INSERT, UPDATE ON NOPK_LOG FROM PUBLIC;

Was diese Prüfung nicht ausschließt: ob diese Statements laufen werden und ob der Benutzer existiert. Zwei Dinge bindet dieser Plan überhaupt nicht: SYSDBA und der Eigentümer eines Objekts unterliegen seinen Privilegien nicht. Eine Anwendung, die noch als einer von beiden verbunden ist, wird also von keinem der Statements hier oben berührt.

Den letzten Teil, „Hält der Plan?“, gibt es, weil sich „dieser Benutzer darf nur seine eigenen Tabellen sehen“ fast immer bei den Rechten irrt, die schon da waren, nicht bei denen, die du vergibst.

Und in den erzeugten Zeilen steckt ein Detail. tbl_* ist ein Präfix: als LIKE-Muster würde TBL_% auch TBLX_OTHER erwischen, weil der Unterstrich in SQL eine Wildcard ist. Der Unterstrich wird also geschützt, und in der Testdatenbank liegt eine Tabelle TBLX_OTHER, die eigens dafür da ist, dass der Test fehlschlägt, wenn irgendwann jemand diese Zeile „vereinfacht“. Genauso bekommt die Prozedur EXECUTE und der View SELECT: den Typ liest das Tool aus dem Katalog. Und die Bezeichner stehen nur dort in Anführungszeichen, wo Firebird es verlangt.

Eine Rolle ist kein Zugriff

Dass in Firebird eine Rolle inaktiv ist, solange die Verbindung sie nicht nennt, ist dokumentiertes Verhalten, und wer mit Rollen gearbeitet hat, weiß das. Ich schreibe es trotzdem hierhin, weil das der Punkt ist, an dem ein lehrbuchmäßig geschriebener GRANT-Plan nichts bewirkt und es niemand merkt.

Best Practice sagt: Privilegien auf die Rolle, Rolle auf den Benutzer. So geschrieben ist dieser Plan auf allen vier Versionen wirkungslos, und ich habe alle vier ausprobiert. Firebird 3.0.14, Plan Zeile für Zeile ohne einen Fehler ausgeführt, und dann die Verbindung so, wie eine Branchenanwendung sie öffnet.

C:\DEV>isql -u APP -p change-this-before-running localhost/3053:C:\DEV\ROLEDEMO.FDB
Database: localhost/3053:C:\DEV\ROLEDEMO.FDB, User: APP
SQL> SELECT * FROM TBL_ORDERS;
Statement failed, SQLSTATE = 28000
no permission for SELECT access to TABLE TBL_ORDERS
SQL>

Genau dieselbe Abfrage, diesmal mit der Rolle in der Verbindung:

C:\DEV>isql -u APP -p change-this-before-running -role APP_ROLE localhost/3053:C:\DEV\ROLEDEMO.FDB
Database: localhost/3053:C:\DEV\ROLEDEMO.FDB, User: APP, Role: APP_ROLE
SQL> SELECT * FROM TBL_ORDERS;

          ID DESCR
============ ==================================================
           1 first order

SQL>

Der Benutzer verliert nur das, was über die Rolle kam: die direkten Rechte und die von PUBLIC bleiben, wo sie sind. Auf einer echten Datenbank liest die Anwendung also die eine oder andere Tabelle trotzdem, sie bleibt beim Start nicht stehen, und das Problem taucht später auf, auf der Maske, die im Januar niemand öffnet.

Eine Rolle zu nennen heißt nicht, sie zu bekommen: ist der Benutzer kein Mitglied, ignoriert Firebird sie, und CURRENT_ROLE antwortet NONE.

Ab 4.0 gibt es dafür GRANT DEFAULT, und auf 4.0.7 und 5.0.4 funktioniert es: mit nur GRANT APP_ROLE TO <Benutzer> wird das SELECT abgelehnt, nach GRANT DEFAULT geht es durch. Eine Merkwürdigkeit gibt es dabei, die du kennen solltest: auch bei aktiver DEFAULT-Rolle antwortet CURRENT_ROLE weiterhin NONE. Das ist einer der Gründe, warum du aus einer SQL-Verbindung heraus eine DEFAULT-Rolle nicht von einer normalen Rolle unterscheidest.

Auf 3.0 gibt es diese Syntax überhaupt nicht, und der Parser bleibt genau hier stehen:

SQL> GRANT DEFAULT APP_ROLE TO USER APP;
Statement failed, SQLSTATE = 42000
Dynamic SQL Error
-SQL error code = -104
-Token unknown - line 1, column 7
-DEFAULT

Die Form des Plans richtet sich also nach der Engine, und der Parameter grant_to erzwingt die eine oder die andere.

Vergleich der erzeugten GRANT-Pläne auf Firebird 2.5/3.0 und auf 4.0/5.0: unter 4.0 bleibt die Rolle wirkungslos, wenn die Verbindung sie nicht nennt, und die Anwendung bekommt „no permission for SELECT access“, ab 4.0 macht GRANT DEFAULT die Rolle auf jeder Verbindung aktiv.
Der Plan, den das Tool erzeugt, ist auf den beiden Engines nicht derselbe, und genau darum geht es.
Auf jeder Firebird-Version kommen die Privilegien, die auf einer Rolle liegen, nicht bei der Anwendung an, solange die Verbindung die Rolle nicht nennt. Ab 4.0 kannst du das mit GRANT DEFAULT umgehen, darunter nicht.

Domains, CHECK und Views

fb_generate_documentation gibt jetzt die Domain aus, auf der eine Spalte aufsetzt, die CHECK-Constraints und, bei einem View, das SELECT, das ihn definiert: die einzige Stelle, an der die Kosten eines Views sichtbar werden, weil sich hinter einem Namen drei Joins und ein Sort verstecken.

Womit fange ich an, wenn die Anwendung schon läuft?

Mit keinem REVOKE. Der erste Schritt fasst nichts an, also kann er auch nichts kaputt machen.

1. Schau nach, sonst nichts. fb_audit_security ohne Parameter sagt dir, was PUBLIC in der Hand hat, und das gilt auch für den neuen Benutzer. Dann dasselbe Tool mit user_name auf den Benutzer, der heute im Einsatz ist.

2. Stell eine Kopie wieder her. Den Plan probierst du dort aus, nicht in Produktion.

3. Mach das Interview auf der Kopie. Wähle app_user_plan aus den Prompts deines Clients und antworte. Es gibt fast immer mehr als eine Objektgruppe: ruf fb_suggest_grants einmal pro Gruppe auf, mit demselben role_name. So wird die Rolle nur einmal angelegt, und die Privilegien summieren sich.

4. Führ den Plan auf der Kopie aus und lass die Anwendung von dort laufen. Das ist der einzige Schritt, der dir sagt, ob etwas kaputtgeht, und es gehen immer dieselben Dinge kaputt: die Lesezugriffe auf die MON$-Tabellen, die von der Anwendung gestarteten Utilities, die beim Start angelegten Objekte, die Stored Procedures außerhalb des Musters. Jeder dieser Fälle ist eine Zeile mehr im Plan, kein Grund umzukehren.

5. Prüf nach, ob die Rolle aktiv ist. Unter 4.0: verbinde dich mit der Kopie, ohne die Rolle zu nennen, so wie es die Anwendung macht. Funktioniert das Lesen, hält der Plan. Bekommst du no permission, gibt es zwei Wege: den Namen der Rolle in den Connection String aufnehmen, oder den Plan mit grant_to=user neu erzeugen und die Privilegien direkt auf den Benutzer legen.

6. PUBLIC ist eine eigene Entscheidung. Diese REVOKE nehmen das Recht jedem weg, der sich verbindet, auch den Programmen, die du vergessen hast. Nicht am selben Abend, an dem du die Anwendung umstellst.

7. In Produktion änderst du eine Zeile. Der neue Benutzer und die Rolle werden neben dem alten Benutzer angelegt, der bleibt, wo er ist, und funktioniert weiter. Du änderst nur den Connection String. Das Rollback ist deshalb, ihn wieder so hinzuschreiben wie vorher, ohne ein einziges Privileg anzufassen. SYSDBA verschwindet dabei nicht: du brauchst ihn weiter für Backup und Administration, er hört nur auf, der Benutzer der Anwendung zu sein.

Wie du es ausprobierst

Lade MCPFirebird-0.6.0-win64.zip aus dem Release auf GitHub, kopiere .env.example nach .env und richte firebird.client_lib auf die fbclient.dll deiner Installation: im Zip findest du sie nicht, mit Absicht, weil nur du weißt, mit welchem Server du redest. Dann registrierst du die Exe als stdio-MCP-Server in deinem Agenten.

Vor dem Release läuft die Suite auf allen vier Engines: 122 Core-Tests plus 95 zur Protokollkonformität. Einer dieser Tests führt den erzeugten GRANT-Plan aus, legt den Benutzer an, verbindet sich ohne Rolle, liest das, was der Plan erteilt hat, und wird bei dem abgewiesen, was er nicht erteilt hat. Ohne den wüsste ich nur, dass das SQL plausibel ist.

Das vollständige Changelog listet die ganze 0.6.0 auf.

Comments

comments powered by Disqus