2011-04-30

Architektur sicherer und wartbarer Datenbank-Anwendungen

Nie mehr Spaghetti als Code
So gerne ich Nudeln mag - in datenbankbasierten Anwendungen haben sie nichts zu suchen. In diesem Beitrag möchte ich beleuchten, wie sie trotzdem oft ihren Weg in den SQL Server finden und wie man sie wieder los wird.
Nudelleserin (c) F. Vahlbruch
In den Standard SQL Server Seminaren wird eine Menge über die Funktionsweise und den sicheren Betrieb von SQL Server vermittelt. Das ist wichtiges Handwerkszeug für jeden Administrator und Entwickler. Was meiner Ansicht nach jedoch oft zu kurz kommt, ist die richtige Architektur von SQL Server Anwendungen. Die Programmierumgebungen (zum Beispiel Visual Studio oder Report Builder) lassen dem Entwickler mehrere Wege offen. Also wird oft der direkte Weg gewählt und die Anwendungen greifen mit SELECT, INSERT, UPDATE oder DELETE direkt auf die Datenbanktabellen zu.

Ich bekenne: Lange Zeit meines Entwicklerdaseins habe ich das so gemacht.

Warum sollte man es auch anders machen?
  1. Weil dieses Vorgehen dazu führt, dass der SQL Code irgendwo in der Anwendung versteckt ist. SQL Code und Andwendungslogik werden miteinander vermengt. Auch wenn es Ihnen als Entwickler zu Beginn noch ganz klar erscheint, so direkt vorzugehen - kennen Sie nicht das Unbehagen, wenn Sie nach einem halben Jahr eine solche Anwendung erweitern müssen?
  2. Weil dieses Vorgehen zu Sicherheitslücken führt:
    • Es verleitet dazu, den SQL Code dynamisch zusammen zu bauen. Das wiederum macht Anwendungen anfällig für SQL Injection. Welch dramatische Sicherheitsprobleme dadurch entstehen können, werde ich in einem meiner nächsten Beiträge zeigen.
    • Die Anwendung erhält direkten Zugriff auf die Tabellen. Wenn ein Angreifer die Verbindungsinformationen, mit denen die Anwendung sich gegenüber dem SQL Server autentifiziert, herausfindet, dann sind die Folgen unabsehbar.
Und wie geht das besser?

Wir ziehen eine Zwischenschicht ein und erlauben externen Anwendungen nur hierauf den Zugriff. Externe Benutzer greifen nicht direkt auf die Tabellen zu, sondern über Views und Stored Procedures.

Wie das Bild zeigt, liegen die für externe Anwender (user_extern) sichtbaren Views und Stored Procedures in einem eigenen Schema, das ich Schnittstelle genannt habe. Die Tabellen hingegen liegen in einem anderen Schema (hier Anwendung genannt), auf das user_extern keine Berechtigungen hat. Der Eigentümer beider Schemas ist der Benutzer user_intern. Dieser erteilt dem von außen zugreifenden user_extern die SELECT-Berechtigung für Views und die EXECUTE-Berechtigung für Stored Procedures.
Dazu noch zwei Anmerkungen:
  • Wenn user_intern dem user_extern die SELECT Berechtigung auf Views erteilt, dann kann dieser "automatisch" auf die Tabellen im Schema Anwendung zugreifen. Technischer Hintergrund ist die hier dokumentierte Besitzkette.
  • Damit die Stored Procedures auf die Tabellen im Schema Anwendung zugreifen können, brauchen sie die Option WITH EXECUTE AS OWNER. Hier finden Sie weitere Informationen.

Was gewinnen wir durch die Zwischenschicht?

  1. Sicherheit - Externe Benutzer haben keine Möglichkeit, direkt auf die Tabellen zuzugreifen. Der Zugewinn an Sicherheit ist beträchtlich.
  2. Übersichtlichkeit - Sie können jetzt in den Views und ganz besonders in den Stored Procedures beliebig komplexe Zugriffslogik programmieren. Denken Sie nur an einen SSRS Bericht mit einem seitenlangen SELECT Statement: Sie programmieren, kommentieren und testen dieses ganz komfortabel in der Stored Procedure. Im Bericht hingegen steht lediglich der Name der Stored Procedure und ihre Parameter. Das ist viel übersichtlicher, als wenn der komplexe SQL Code im Dataset des Berichts stehen würde.
  3. Wartbarkeit - Wenn das SQL Statement überarbeitet werden muss, weil sich zugrunde liegende Tabellen geändert haben, dann brauchen Sie lediglich die Stored Procedure zu ändern. Der Bericht bleibt unverändert.
  4. Aufgabenteilung - Beim Arbeiten in einem größeren Team können Sie die Aufgaben besser trennen: SQL Programmierer bearbeiten Views und Stored Procedures. Berichtsdesigner bearbeiten Berichte. Anwendungsentwickler arbeiten mit ihrer vertrauten Hochsprache.
Viele Gründe also, Spaghetticode loszuwerden und durch eine übersichtliche, sichere Architektur abzulösen.

2011-04-09

Fehler bei Verwendung von Unterberichten

Gestern habe ich zum ersten Mal einen Anwendungsfall für den Einsatz von Unterberichten mit Reporting Services gesehen. Bisher konnte ich - im Unterschied zum Arbeiten mit Access - alle Berichtsanforderungen mit dem Tablix-Element abdecken. Nun aber hatte ein Kunde die Anforderung, Formblätter zu erstellen. Bei Formblättern ist die Anordnung der Ausgabefelder abhängig vom Typ des zu dokumentierenden Objekts.
Überträgt man diese Anforderung auf die AdventureWorks Datenbank, könnte man sich folgende Aufgabenstellung vorstellen: Zu jeder Rechnungsposition sollen Detailinformationen dargestellt werden. Das Layout der Detailinformationen von Fahrrädern soll völlig anders gestaltet sein als das Layout der Detailinformationen von Bekleidung.
Die Idee
In einem Listenelement wird abhängig davon, ob die Detailinformationen zu "Bikes" oder "Clothing" gehört, ein Unterbericht dargestellt.

Listenelement mit zwei Unterberichten
So weit, so gut. Nun wollten wir nur noch über die Eigenschaft "Hidden" eines jeden Unterberichts mit Hilfe einer Expression dafür sorgen, dass immer nur einer dieser Unterberichte im Bericht angezeigt wird.
Die Überraschung
Das funktioniert nicht! Die Steuerung der Sichtbarkeit funktioniert immer nur für den ersten Unterbericht - der zweite wird nie sichtbar.
Die Lösung
Wir haben über eine "AND" Bedingung in der WHERE Klausel des Subreport-Datasets dafür gesorgt, dass das Dataset im Unterbericht "Bikes" keine Datensätze enthält, wenn die Rechnungsposition kein "Bike" ist. Ebenso haben wir dafür gesorgt, dass das Dataset im Unterbericht "Clothing" keine Datensätze enthält, wenn die Rechnungsposition kein "Clothing" ist.
Auf diese Weise rendert SSRS den Bericht wie gewünscht.