Überblick
SQL Server Ledger ist ein Service in MS SQL Server, durch den Daten- und Transaktionsintegrität gewahrt werden. Durch das Ledger-Feature ist eindeutig nachvollziehbar, welche Änderungen an einer Tabelle oder Datenbank vorgenommen wurden. Die Richtigkeit der Ledger-Tabelle kann auch durch das Feature validiert werden. Die Ledger-Funktion ist vor allem interessant für Szenarien, in denen mehrere Instanzen Zugriff auf oder Einsicht in eine Datenbank benötigen, etwa in Lieferketten, die mehrere Betriebe betreffen. Außerdem erleichtert Ledger das Nachvollziehen der Daten- und Transaktionsintegrität bei einem Audit. Das wird erreicht durch die Verwendung einer Merkle-Struktur (Hash-Baum) und Blockchain-Technologie.
Eine Blockchain ist eine stetig wachsende Kette von gehashten Blocks, durch die sichergestellt wird, dass Daten bei ihrer Übertragung nicht manipuliert wurden. Blockchains finden vor Allem im Handel mit Kryptowährungen Verwendung. In diesem Zusammenhang sind Blockchains dezentralisiert, das bedeutet, dass die Informationen nicht an einer Stelle gespeichert, sondern bei allen Teilnehmern der Transaktion verteilt gespeichert sind. Das erfordert hohe Rechenleistung und bringt mitunter hohe Latenzen mit sich.
Durch Ledger kann die sichere Off-Chain-Speicherung der Daten in einer Blockchain gewährleistet werden, was die Performanz von Blockchain-Anwendungen deutlich erhöht, indem die Datenmenge, die in der Blockchain selbst gespeichert wird, reduziert wird. Blockchain-Technologie wird verwendet (stetig wachsende Kette aus Hashes, die keine Änderung zulassen), aber sie wird zentralisiert angewendet statt dezentralisiert. Das bedeutet für uns, dass wir übliche Performance-Probleme wie hohe Latenz oder geringer Durchsatz, mit Blockchain umgehen können.
Funktionsweise
Für jeden Eintrag, der in der Datenbank hinzugefügt oder gelöscht wird, wird die frühere Version des Eintrags in der Verlaufstabelle gespeichert. Außerdem wird eine Transaktions-ID zugewiesen und eine Sequenznummer generiert. Der Inhalt des Eintrags wird serialisiert und beim Berechnen des Hashes für alle Einträge eingefügt, die von dieser Änderung betroffen sind. Bei Aktualisierung (Update) einer Zeile wird die alte Version einer Zeile gelöscht und die neue Version eingefügt, weswegen wir im Folgenden nur noch von Einfüge- und Löschvorgängen reden.
Für jede Tabelle wird eine separate Merkle-Struktur verwendet, deren Einträge auf Transaktionsebene gespeichert werden. Es wird für jede Zeilenversion ein Hash berechnet, der neben dem Zeileninhalt auch Metadaten beinhaltet. Im nächsten Schritt wird dieser Hash mit dem der nächsten Zeile konkateniert und gehasht. Diese neuen Hashes bilden die darüberliegende Ebene des Baumes der Merkle-Struktur und der Prozess von Konkatenation und Hashing wird wiederholt, bis in der obersten Ebene ein einziger Eintrag (Root) übrig bleibt.
Mehrere Transaktionen werden in einem Block zusammengefasst, der dann verarbeitet wird. Ein Block wird ungefähr alle 30 Sekunden geschlossen, wenn Nutzer:innen manuell einen Datenbankdigest über sys.sp_generate_database_digest generieren oder wenn 100 000 Transaktionen darin enthalten sind.
Wenn ein Block generiert wird, werden alle darin enthaltenen Transaktionen abgerufen, dann der Merkle-Strukturbaum unter Einbeziehung des vorherigen Blocks berechnet und schließlich der geschlossene Block in der Systemkatalogsicht sys.database_ledger_blocks gespeichert. Dieser Vorgang verlangsamt allerdings nicht die Transaktion, da die Hash-Berechnung asynchron erfolgt. Es ist wichtig, die Begriffe “Block” und “Digest” voneinander abzugrenzen: Der Datenbankdigest ist der Hashwert des letzten Blockes, der verarbeitet wurde.
Anwendung und Sicherung in Azure
Beim Erstellen einer neuen Datenbank in Azure können wir die Option eines Ledgers konfigurieren. Die Ledger-Funktion ist nicht verfügbar, wenn wir eine Datenbank mit Sample-Daten oder einem Backup einer anderen Datenbank füllen.
In der Konfiguration können wir auch einen Sekundärspeicher für die Hashes auswählen, um die Sicherheit zu erhöhen, zum Beispiel Azure Storage oder Azure Confidential Ledger.
Wenn wir Azure Storage wählen, sollten wir darauf achten, in den Zugriffsrichtlinien die Unveränderlichkeit auszuwählen, um größtmögliche Manipulationssicherheit zu gewährleisten. Im Speicherkonto ist diese Einstellung im Dropdown-Menü des Containers zu finden.
Es gibt zwei Arten von Unveränderlichkeitsrichtlinie: Aufbewahrung für juristische Zwecke und zeitbasierte Aufbewahrung. Wir müssen zeitbasierte Aufbewahrung verwenden, denn Azure SQL Ledger benutzt Append-Only Blobs, um die Hashes zu speichern und diese werden nur von zeitbasierter Retention unterstützt. Wenn die Zeit, die in der zeitbasierten Aufbewahrung spezifiziert wird, abgelaufen ist, können die Dateien nicht mehr verändert werden.
Alternativ können wir die Ledger-Funktion innerhalb eines SQL-Kommandos beim Erstellen der Datenbank aktivieren.
Arten von Ledger Tables
Wir können die Ledger-Funktion für eine ganze Datenbank oder für einzelne Tabellen in einer Datenbank aktivieren. Ledger-Tabellen können Updatable und Append-Only sein. Für jede Updatable Ledger-Tabelle, die angelegt wird, werden drei Objekte erschaffen: Die Tabelle selbst, die History-Tabelle und eine View.
Für eine Append-Only-Tabelle gibt es keine Verlaufstabelle, weil in dieser nur geänderte, also effektiv gelöschte alte Versionen der Zeilen gespeichert werden und solche gibt es in diesem Fall nicht.
Hier sehen wir, dass durch das obige SQL-Kommando neben “LedgerTableTest” zusätzlich eine Verlaufstabelle und eine Ansicht erstellt wurden und die Ansicht wurde entsprechend unseres Kommandos benannt.
Hier sehen wir, dass neben den Einträgen selbst auch die Spalten Transaktions-ID, Sequenznummer, Art der Operation und Beschreibung der Operation (Einfügen) erstellt wurden. Alle drei Vorgänge sind Teil derselben Transaktion mit der ID 1703 und wir können sehen, dass jeder Einfügevorgang innerhalb der Transaktion eine eigene Sequenznummer erhält.
Die Verlaufstabelle ist zu diesem Zeitpunkt immer noch leer, da bisher keine Einträge gelöscht wurden.
Jetzt ändern wir den ersten Eintrag.
Die Verlaufstabelle speichert die alte (gelöschte) Version der Zeile. Die Ansicht zeigt uns, dass es zwei Vorgänge in der Transaktion mit ID 1775 gab: Einen Löschvorgang der ursprünglichen Zeile und einen Einfügevorgang der aktualisierten.
In der Übersicht über die Tabellen der Datenbank können wir sehen, dass “LedgerTestTable” vier zusätzliche Spalten hat:
- [ledger_start_transaction_id]: ID der letzten Transaktion, die diesen Eintrag verändert hat und ihm die aktuellen Werte gegeben hat
- [ledger_end_transaction_id]: ID der Transaktion, die diesen Eintrag zu einem anderen gemacht hat. Wird in dieser Tabelle nie befüllt, nur in der Verlaufstabelle
- [ledger_start_sequence_number]: Sequenznummer des Vorgangs innerhalb der Starttransaktion
- [ledger_end_sequence_number]: Sequenznummer des Vorgangs innerhalb der Endtransaktion. Wird in dieser Tabelle nie befüllt, nur in der Verlaufstabelle
Wir müssen bei Erstellung einer Append-Only-Tabelle (“nur Anfügen”) keine Systemversionsverwaltung aktivieren. Außerdem wird hierbei keine Verlaufstabelle erstellt, da es keine Einträge dafür gibt. Es werden nur neue Einträge hinzugefügt, und die Verlaufstabelle dient dazu, gelöschte und geänderte Einträge in der Tabelle zu verzeichnen. Die gibt es hier natürlich nicht. Folglich gibt es auch die “end”-Felder der Tabelle “LedgerTestTableAppendable” nicht, die in der Updatable-Version vorhanden waren.
Verifizierung der Datenbank in Azure Portal
Ein Datenbankdigest ist der Hashwert des letzten Blockes, der verarbeitet wurde. Er repräsentiert den letzten aktuellen Zustand aller Ledgertabellen in einer Datenbank. Digests können automatisch oder manuell generiert werden. Bei der Verifizierung wird der Hashwert berechnet und mit dem letzten gespeicherten Digest verglichen. Wenn beide Werte übereinstimmen, liegen die Daten in dem Zustand vor, den wir erwarten und wurden nicht manipuliert.
Es ist wichtig, die Unveränderlichkeitsrichtlinien des Speichers festzulegen, damit niemand (auch kein Datenbankadministrator) etwas an den gespeicherten Digests ändern kann.
Simulieren eines Fehlers/Datenmanipulation
Unter welchen Umständen ist es möglich, die Daten dennoch zu manipulieren? Wir können tatsächlich die Daten nicht verändern, ohne dass Ledger diese Änderungen aufzeichnet. Die einzige Möglichkeit, dies zu umgehen, besteht darin, die Digest-Dateien im Sekundärspeicher zu verändern. Und selbst wenn hier eine Manipulation stattgefunden hat, wird sie während der Validierung durch Ledger offengelegt.
Darum ist es so wichtig, beim Erstellen der Datenbank und bei der Auswahl des Sekundärspeichers die Unveränderlichkeit zu setzen. Nur dann können auch die Digest-Dateien nicht manipuliert werden.
Fazit
SQL Server Ledger ist eine sehr sichere und effiziente Methode, die Daten- und Transaktionsintegrität von Datenbanken zu wahren, vor Allem gegenüber Dritten wie Audit-Instanzen. Durch Ledger können Unternehmen ihren Kunden und Geschäftspartnern eine sehr hohe Sicherheit vor Manipulation garantieren und das Vertrauen stärken.