Aliasse, Katalog und die zentrale Regel
Der Datendienst erzeugt sein SQL selbst – aus einem Alias statt eines Datenbanknamens, aus Namen, die er im Katalog der Datenbank nachschlägt, und aus Werten, die er als Parameter bindet. Diese Seite erklärt das Modell, auf dem jede Anfrage beruht, und die zwei Wege für Anweisungen, die der Aufrufer selbst schreibt: geprüfte Anweisungen, die der Dienst zerlegt und Objekt für Objekt prüft, und die ungeprüfte Durchreiche, die getrennt freizuschalten ist.
Wichtige Funktionen
- Datenbanken erscheinen nach außen nur unter freigegebenen Aliasnamen.
- Jeder Bezeichner wird gegen den Katalog geprüft, jeder Wert als Parameter gebunden.
- Anweisungen des Aufrufers auf zwei Wegen: geprüft – zerlegt, jedes Objekt gegen Katalog und Regeln – oder ungeprüft auf einem eigens freigeschalteten Weg, begrenzt durch die Rechte der Datenbankanmeldung.
Grundkonzepte
Aliasse statt Datenbanknamen
Datenbanken werden dem Aufrufer nie mit Namen genannt, sondern über freigegebene Aliasnamen angesprochen; die Zuordnung auf Verbindungszeichenfolgen geschieht in der Konfiguration dieses Dienstes.
Aufrufer werden ebenso nicht über Ports getrennt, sondern über ihre Zugangsdaten und den
ihnen zugeteilten Umfang: Alle erreichen den Dienst über denselben HTTPS-Port (Vorgabe 8433,
angelehnt an 1433 von SQL Server). Der HTTP-Port 8434 antwortet nur auf dem eigenen Rechner,
für Werkzeuge, Gesundheitsprobe und einen vorgeschalteten Webserver.
Die zentrale Regel
Der Dienst führt kein SQL aus, das er nicht selbst erzeugt oder geprüft hat. Er erzeugt SQL – aus einem typisierten Vertrag mit freigegebener Ressourcenkennung, ausgewählten Feldern, strukturierten Filterknoten, Sortierangaben und Seitenangaben. Eine Anweisung, die der Aufrufer schreibt, führt er nur aus, nachdem er sie zerlegt und jedes Objekt geprüft hat (geprüfte Anweisungen). Die einzige Ausnahme ist die ungeprüfte Durchreiche, die eigens freizuschalten ist.
Abgewiesen werden:
| Abgewiesen | Warum |
|---|---|
| SQL des Aufrufers, das der Dienst nicht zerlegen und prüfen kann – außer auf der freigeschalteten Durchreiche | Die Prüfstelle wäre damit aufgehoben |
| Ungeprüfte Tabellen- oder Spaltennamen | Bezeichner lassen sich nicht parametrisieren, nur gegen eine Freigabeliste prüfen |
| Freie Sortierausdrücke | Derselbe Grund |
| Frei wählbare Prozedurnamen | Sonst wird jede Prozedur der Datenbank aufrufbar |
| Abruf ohne Obergrenze | Ein einzelner Aufruf könnte sonst den Dienst belegen |
Alle Werte werden parametrisiert. Alle Bezeichner werden gegen freigegebene Angaben geprüft. Das sind zwei verschiedene Schutzmechanismen, und beide sind nötig.
Die eine Ausnahme: der Durchreichepfad
Über POST /api/v1/cf/{alias}/sql.json nimmt der Dienst eine Anweisung entgegen, die der
Aufrufer selbst geschrieben hat, und führt sie aus. Das ist keine Aufweichung der Regel,
sondern ein getrennt freizuschaltender Weg – und bewusst der einzige, auf dem der Dienst einen Text
ausführt, ohne ihn zu beurteilen.
Warum es ihn gibt. Ein Client, der immer nur ein Objekt auf einmal adressieren kann, kann einen Verbund, eine Gruppierung oder eine mengenbasierte Änderung gar nicht ausdrücken. Er müsste alle beteiligten Zeilen zu sich holen und die Arbeit lokal tun, statt nur das Ergebnis zu empfangen; der Aufwand wächst mit der Größe der Tabellen, nicht mit der des Ergebnisses. Genau dafür kennen Datenbankwerkzeuge die durchgereichte Abfrage.
Was ihn begrenzt – und was nicht. Der Dienst liest die Anweisung nicht: Er zerlegt sie
nicht, prüft sie nicht gegen ein Muster und stuft sie nicht als lesend oder schreibend ein.
Eine solche Prüfung wäre eine Zusage, die dieser Dienst nicht halten kann; SELECT allein
sagt nichts darüber, was über Sichten, Funktionen und Rechteketten erreichbar ist. Die Grenze
ist deshalb dort, wo sie auch durchgesetzt wird: bei den Rechten der Anmeldung, unter der
die Anweisung läuft. Was diese Anmeldung darf, darf eine durchgereichte Anweisung – Lesen,
Schreiben und Schemaänderungen eingeschlossen. Welche Anmeldung das ist, entscheidet die
Anmeldung am Dienst (siehe Weitergabe der Identität):
| Aufrufer | Anweisung läuft als | Zugang zur Durchreiche |
|---|---|---|
Dienstschlüssel; Windows-Konto an einem Alias ohne Persons: Impersonate | Anmeldung des Alias | Regel nennt sql wörtlich |
| SQL-Anmeldung (HTTP Basic) | eigene Verbindung dieser Anmeldung | eine nicht nur lesende Regel für den Alias genügt |
Windows-Konto oder persönlicher Schlüssel mit eingetragener SQL-Anmeldung an einem Alias mit Persons: Impersonate | EXECUTE AS LOGIN der Person | eine nicht nur lesende Regel für den Alias genügt |
gewählte Datenbank-Identität der Art Login oder Own | EXECUTE AS LOGIN der Sammel- bzw. eigenen Anmeldung | eine nicht nur lesende Regel für den Alias genügt |
In den Fällen ohne sql begrenzen die Objektlisten der Regel die Anweisung nicht; sie läuft mit allen
Rechten dieser Anmeldung.
Unter der eigenen Anmeldung prüft SQL Server jede Anweisung gegen deren Rechte – genau wie
bei einer direkten Verbindung über Microsofts ODBC-Treiber. Das Recht sql käme dort nichts
hinzu, würde aber jedem Anwender ohne dieses Recht die Verbünde, Gruppierungen und
Funktionsaufrufe verwehren, die Access als eigene Anweisungen schickt – jede gespeicherte Abfrage
über mehrere verknüpfte Tabellen gehört dazu. Unter der Anmeldung des Alias begrenzt nichts außer der Regel die
ungeprüfte Anweisung; dort bleibt sql die einzige Tür zu ihr. Wer sql nicht hat, schickt
stattdessen geprüfte Anweisungen. Ein Windows-Konto ohne eigene Anmeldung in SQL Server
wird an einem Alias mit Persons: Impersonate beim Öffnen der Sitzung abgewiesen (403) und läuft nie
ersatzweise unter der Anmeldung des Alias. Ein persönlicher Schlüssel, dessen Person keine SQL-Anmeldung
eingetragen hat, läuft dagegen unter der Anmeldung des Alias; für ihn gilt dann die erste Zeile der
Tabelle.
Immer gilt zusätzlich, sonst antwortet der Dienst mit 403:
| Bedingung | Wo sie steht |
|---|---|
| Der Alias nimmt eigene Anweisungen an | AllowPassThrough: true am Eintrag der Datenbank |
| Der Aufrufer ist nicht nur lesend | ReadOnly: false |
Ein vorhandenes * unter Resources schaltet den Pfad nicht frei. Das * ist eine
Aussage über die Objektarten des Katalogs; dürfte es auch „und jede Anweisung, die du
schreibst“ bedeuten, hätte sich jede vorher geschriebene Regel in dem Moment erweitert, in dem
es diesen Pfad gibt. Ein nur lesender Aufrufer wird ganz abgewiesen und nicht etwa auf lesende
Anweisungen eingeschränkt – der Dienst kann die beiden nicht unterscheiden, also heißt nur
lesend hier: keine eigenen Anweisungen. Beide Widersprüche fängt die Startprüfung ab: Nennt
eine Regel sql und ist zugleich nur lesend, startet der Dienst nicht.
Geprüfte Anweisungen: ohne das Recht sql
Access schickt Verbünde, Unterabfragen, Summen über mehrere Tabellen, Funktionen und
Mengenänderungen über verknüpfte Tabellen als eine Anweisung und rechnet sie nicht selbst, wenn
der Treiber sie abweist. Mit Microsofts ODBC-Treiber laufen sie, weil SQL Server die Rechte prüft.
Damit das über den Dienst ebenso geht, ohne jedem Anwender das Recht sql zu geben, nimmt der Dienst
an derselben Adresse geprüfte Anweisungen an – von jedem Aufrufer, dessen Regeln den Alias nennen,
wenn der Alias es zulässt (AllowCheckedStatements, Vorgabe an).
Geprüft heißt: Der Dienst zerlegt die Anweisung mit dem T-SQL-Parser von Microsoft und lässt sie nur zu, wenn
- jede ihrer Anweisungen eine dieser ist:
SELECT(mit Verbund, Unterabfrage,UNION,WITH, Gruppierung, Fensterfunktionen),INSERT,UPDATE,DELETEoder der Aufruf einer Prozedur mit Namen. Mehrere Anweisungen in einem Stapel (SELECT …; SELECT …) laufen wie mit Microsofts ODBC-Treiber; jede wird für sich geprüft, die TrennzeileGOwird abgewiesen; - jede ihrer Formen in einer abschließenden Liste steht – keine Schemaänderung, kein dynamisches SQL, kein
SELECT … INTO, keine Variable außer den gebundenen Parametern, keine Namen mit Datenbank oder Server, keine Funktion, die den Server, die Anmeldung oder andere Objekte beschreibt; - jedes genannte Objekt im Katalog des Alias steht und für das, was die Anweisung damit tut, nach denselben Regeln freigegeben ist wie eine Anfrage auf das Objekt selbst: Lesen wie
GET, Anlegen wiePOST, Ändern wiePATCH, Löschen wieDELETE, eine Prozedur ausführen wiePOST. Das Ziel einer Änderung braucht zusätzlich das Leserecht, weil eine Bedingung über seine Spalten Werte verrät. Rechte je Objekt (Objects,ExceptObjects) gelten ebenso.
Ausgeführt wird nicht der Text des Aufrufers, sondern die neu geschriebene Fassung, in der jedes
Objekt mit dem Schema steht, gegen das der Dienst geprüft hat. Ein Aufrufer erreicht so nichts, was er
nicht auch Objekt für Objekt über OData erreichen dürfte; er spart nur, alle Zeilen zu sich zu holen.
Eine nur lesende Regel lässt nur lesende Anweisungen zu. Wer das Recht sql hat oder unter eigener
Anmeldung arbeitet, bekommt weiter die ungeprüfte Durchreiche.
Große Ergebnisse werden gestreamt. Liefert eine Anweisung mehr als 500 Zeilen, beginnt die
Antwort (JSON) mit den ersten Zeilen, während SQL Server noch die übrigen erzeugt; kleinere
Ergebnisse werden gesammelt beantwortet. Beide Formen sind dasselbe Dokument – columns zuerst,
dann data, dann die Zählungen. Die Antwort beginnt damit etwa dann, wenn SQL Server die erste
Zeile liefert, statt auf die letzte zu warten. Scheitert die
Anweisung nach Beginn der Antwort, trennt der Dienst die Verbindung: Der Status 200 ist
dann schon gesendet, und ein ordentlich beendetes, aber verkürztes Dokument sähe wie ein
vollständiges aus. Jeder Leser meldet das abgebrochene Dokument als Fehler; die Ursache steht im
Protokoll unter der Korrelations-ID. Scheitert sie innerhalb der ersten 500 Zeilen, antwortet der
Dienst mit dem passenden Status. Liest der Aufrufer nicht zu Ende und schließt die
Verbindung, bricht der Dienst die Anweisung in SQL Server ab, statt den Rest zu lesen und zu verwerfen.
Die XML-Darstellung wird weiterhin gesammelt.
Der Text steht im Audit-Strom, vor der Ausführung, unter derselben Korrelations-ID wie der Ausgang der Anfrage. Werte bleiben draußen – sie sind die Daten. Sonst enthält dieser Strom keine Anweisungen, weil dort die Ressource genügt: Was geschehen ist, folgt aus ihr. Hier ist der Text die einzige Beschreibung dessen, was geschehen ist.
Bewusst nicht angeboten
Drei naheliegende Wege bietet der Dienst bewusst nicht an:
| Weg | Warum nicht |
|---|---|
| OData der Protokollfassungen vor Version 4 (Atom) | Der Dienst spricht die OData-v4-Oberfläche. Version 4 beschreibt sich selbst mit Typen, Facetten und Schlüsseln; die Office-Clients lesen sie |
| Verbindung aus der Adresse (Datenbankname oder Server in der URL) | Würde die Verbindung aus der URL bauen und widerspräche damit dem Aliasmodell, dessen Zweck gerade ist, dass ein Aufrufer die Datenbank nicht wählen kann |
| Andere Datenbanksysteme als SQL Server ohne Provider | Jedes Datenbanksystem braucht einen Provider; ein Alias, dessen Provider nicht geladen ist, verhindert den Start, statt als leerer Rumpf angenommen zu werden |