zurück zum Artikel

Blockchain und DSGVO müssen kein Widerspruch sein

Know-how
Blockchain-Technologie braucht gesetzliche Grundlagen

(Bild: Pushish Images / shutterstock.com)

Bei der Blockchain-Entwicklung ist nicht nur von neuen Frameworks und Features die Rede, auch regulatorische und Compliance-relevante Anliegen spielen eine Rolle. Einer der Bereiche ist die Datenschutzgrundverordnung (DSGVO), die seit Mai 2018 Anforderungen an den Schutz personenbezogener Daten formuliert.

Die DSGVO besteht aus 99 Artikeln. Viele davon beschreiben den Anwendungsbereich und das Inkrafttreten; oder sie formulieren, wie staatliche Einrichtungen organisiert sein sollen. Alle anderen Artikel betreffen allgemein datenverarbeitende Unternehmen. Der Artikel beschränkt sich auf die technische Sicht, organisatorische Rahmenbedingungen bleiben deswegen außen vor. Als Beispiel: Für eine Software ist es nicht wichtig, ob Firmen ab einer bestimmten Größe einen Datenschutzbeauftragten haben müssen. Selbstverständlich ist das fürs Unternehmen relevant; nur eben nicht für die technische Umsetzung der Software.

Mit dieser Auswahl bleiben zehn Artikel übrig, die so für jede Software relevant sind, die personenbezogene Daten verarbeitet. Schränkt man sie noch einmal auf die Relevanz für die Blockchain ein, dann schaut man darauf, inwieweit sich ein Programm auf einer Blockchain grundsätzlich anders verhält als mit anderen Techniken – in der Regel mit einer Datenbank.

Solange man von Turing-vollständigen Blockchains wie Ethereum ausgeht, sind sieben der zehn Artikel mit herkömmlichen Methoden zu implementieren. Sie sind hier kurz beschrieben:

Alle diese Artikel sind wie gesagt für jede Anwendung umzusetzen. Für Blockchain-Projekte bleiben drei Artikel der DSGVO übrig, für die man andere Lösungsansätze braucht:

Überblick der Artikel

Das Recht auf Datenaktualisierung in Artikel 16 beschreibt die Möglichkeit, Daten zu aktualisieren. Es muss möglich sein, einen alten, falschen Datensatz durch einen neuen zu ersetzen. Je nach Auslegung bedeutet das die Löschung der alten Daten; um auf Nummer sicher zu gehen, hält man sich an diese strengste Auslegung. In ihrem Fall ist die Umsetzung auf der Blockchain aufgrund der Unveränderbarkeit bestehender Daten nicht ganz trivial, ist aber ohnehin aufgrund von Artikel 17 umzusetzen.

Artikel 17 beschreibt das Recht auf Vergessen. Es wird gerne herangezogen, um die Unmöglichkeit einer DSGVO-konformen Blockchain-Anwendung zu belegen; schließlich ist die Manipulationssicherheit der Daten eines der Hauptargumente für Blockchain-Techniken. In der Beispielimplementierung zeigt der Autor einige Möglichkeiten, diese und die beiden vorher beschrieben Anforderungen zu erfüllen. Der Ansatz besteht aus den drei Komponenten Hash, Secret und Merkle-Tree (Hash-Baum); dieses Vorgehen lässt sich mit zk-SNARKs als viertem Baustein kombinieren.

Hierbei werden keine personenbezogenen Daten direkt auf der Blockchain gespeichert, sondern mit den einzelnen Bausteinen an eine Off-Chain-Datenbank geknüpft. Durch die im Folgenden beschriebene Konstruktion lässt sich die Korrektheit der Daten beweisen, ohne dass Dritte unerwünscht Zugang erhalten.

Artikel 32 beschäftigt sich mit dem Schutz personenbezogener Daten. Der Aspekt hat eine besondere Blockchain-Relevanz, da bei öffentlichen Chains die gesamten Daten öffentlich zugänglich sind. Folglich bedarf der Schutz einer besonderen Beachtung.

Erster Baustein: Hashing

Der erste Baustein ist das Hashen der Daten und das anschließende Speichern des Hashes in der Blockchain. Dadurch wird ein direktes Speichern der personenbezogenen Daten verhindert. Die Verwaltung kann genau wie bisher das Unternehmen selbst "off-chain" übernehmen. Ein Hash ist als Fingerabdruck einer Datei oder einer beliebigen anderen Zeichenabfolge zu verstehen. Es wurde eine kryptografische Hashfunktion mit entsprechend höherer Garantie für ihre Sicherheit benutzt. Zusätzlich sieht der Hash schon bei kleinsten Abweichungen in der Zeichenkette vollkommen anders aus. Aufgrund der guten Unterstützung im Ethereum-Ökosystem kam hier die Hashfunktion Keccak-256 zum Einsatz.

Da auch die weiteren Teile der Implementierung fortwährend einen Hash mit der gleichen Länge in die Blockchain schreiben, lässt sich immer der gleiche Contract benutzen. Er könnte alle Aufgaben übernehmen, die auch eine herkömmliche Ethereum-Identität unterstützt, nur eben nicht in Form einer Adresse, sondern als Hash. Ein Beispiel-Contract, der nur Kontostände für Kunden führt, könnte beispielsweise wie folgt aussehen:

// Legt die Compilerversion fest
pragma solidity 0.5;

// Deklariert den Smart-Contract Sparbuch
contract Sparbuch {

// Adresse des Besitzers, dient der Rechteverwaltung
address owner;

// Beinhaltet alle Kontostände der durch Hashes referenzierten Kunden, public erzeugt automtisch eine Getter-Methode für die Variable
mapping(bytes32 => uint256) public balances;

// Beinhaltet alle registrierten Kunden
mapping(bytes32 => bool) public registered;

// Beim Erstellen des Contracts wird der Ersteller als Besitzer festgelegt
constructor() public {
owner = msg.sender;
}

// Verhindert, dass bestimmte Funktionen durch fremde Adressen aufgerufen werden können
modifier onlyOwner() {
require(owner == msg.sender);
_;
}

// Verhindert, dass bestimmte Funktionen für nicht registrierte Kunden aufgerufen werden können
modifier isRegistered(bytes32 _client) {
require(registered[_client]);
_;
}

// Verändert den Kontostand eines bestimmten Kunden
function addValue(bytes32 _client, uint256 _value) public
onlyOwner isRegistered(_client) {
uint256 newBalance = balances[_client] + _value;
// Stellt sicher, dass kein Overflow passieren kann
require(newBalance >= balances[_client]);
balances[_client] = balances[_client] + _value;
}

// Verändert den Kontostand eines bestimmten Kunden
function deductValue(bytes32 _client, uint256 _value) public
onlyOwner isRegistered(_client) {
// Stellt sicher, dass kein Overflow passieren kann
require(balances[_client] >= _value);
balances[_client] = balances[_client] - _value;
}

// Registriert einen neuen Kunden
function registerClient(bytes32 _client) public
onlyOwner {
registered[_client] = true;
}
}

Hier nun ein Beispiel für das Erstellen eines Hashes mit anschließender Sicherung in der Blockchain:

// Nutzt die web3-Bibliothek, zu installieren mit: npm i web3
const web3 = require('web3')

// Importiert Informationen wie ABI und Adresse eines Smart Contract, mit dem interagiert werden soll
const contractData = require('path/to/abi')

// Die personenbezogenen Daten, auf die die Referenz gebildet werden soll
let value = 'Max Mustermann, 01577123456, Musterstraße 42, 04321 Musterstadt'

// Das Hashen der Daten
// Hier lautet der Hash: 0x168eaaa61a5a7ff7e59880cbbb7d23e9267b8b7b96cd9a4a5e7dfd7f5e2a301d
let hashedValue = web3.utils.sha3(value)

// Erstellt einen web3 Provider, der eine Verbindung zur Blockchain über einen lokalen Node hat
let web3Connection = new web3("ws://localhost:8546")

// Erstellt ein Contract-Objekt, mit dem interagiert werden kann
let contract = new web3Connection.eth.Contract(contractData.abi, contractData.address)

contract.methods.registerClient(hashedValue).send()
.then((result)=>{
if(result.status === true){
// Speichern des Hashes in die Blockchain war erfolgreich, jetzt könnte eine lokale Datenbank aktualisiert werden
} else {
// Es gab ein Problem im Contract, d.h., der Hash wurde nicht gespeichert, aber die Transaktion ist in der Blockchain angekommen, es muss entsprechend mit dem Fehler umgegangen werden
}
})
.catch((error)=>{
// Der Hash wurde nicht gespeichert, es ist ein Fehler aufgetreten, Grund dafür könnte zum Beispiel eine fehlerhafte Verbindung zur Blockchain sein, es muss entsprechend mit dem Fehler umgegangen werden
})

Nächster Schritt: Secret

Ein einfacher Hash lässt sich bei einem bekannten, begrenzten Suchraum mit moderner Hardware schnell finden. Ein direktes Nachvollziehen, welcher Hash welchen Kunden darstellt, ist also einfach. In der Beispielimplementierung wird das mit dem Hinzufügen eines Secrets zu den zu hashenden Daten gelöst. Ist es groß genug, macht es das Erraten des Hashes unmöglich, wie man am Hash mit Secret (in JavaScript) sieht:

const web3 = require('web3')


//Größe des Secrets, je größer, desto schwieriger ist das Erraten des zu schützenden Wertes
let secretSize = 64

//Generiert ein zufälliges Secret, z.B.: 0x507f251c9d0a65dc9064f005e1cf099f22b7b881a3e19815dcdfc9d77b8c3e5d7146ffc6a7ff1066425568b0b2ac6e3e6bbaea2c2d6819279c4b250ba6e568ce
let secret = web3.utils.randomHex(secretSize)

//Der zu schützende Wert
let value = 'Max Mustermann, 01577123456, Musterstraße 42, 04321 Musterstadt'

//Das Endergebnis, zu speichern auf die Blockchain, z.B.: 0x83316cb9645f7857d30771051be16b78a6fe9ffaa5d8806bf6858888447b97be
let hashedValue = web3.utils.sha3(secret + value)

Ein weiteres Problem ist, dass die alleinige Verwendung der Blockchain unter Umständen keinen Vorteil bringt. Beispielsweise wird Transparenz, eine der großen Stärken der Blockchain, komplett ausgehebelt, wenn niemand außer dem Verwalter der Daten einsehen kann, was im System passiert. Um diesen Vorteil nutzen zu können, muss wenigstens noch ein weiterer Teilnehmer über relevante Vorgänge Bescheid wissen. Wird im Auftrag von Kunden oder unter der Aufsicht eines Regulators gearbeitet, ist ein Nachweis nützlich, hinter welchem Hash sich welcher Kunde verbirgt.

Möglich wird das, indem der Verifizierungsstelle das Secret ausgehändigt wird. Der Verifizierer kann nun mit den ihm bekannten Daten und dem Secret den Hash errechnen, also nachvollziehen, was in Zusammenhang mit dem Kunden getan wurde. Eine klare Schwäche dieses Mechanismus ist, dass das Secret verraten werden muss. Das wird über irgendeinen Kanal geschehen, der wahrscheinlich nicht ganz sicher ist. Es ist also davon auszugehen, dass ein Dritter das Secret erfahren und damit wieder einfach herausfinden kann, wer sich hinter dem zugehörigen Hash befindet.

Nächster Schritt: Merkle-Tree

Eine teilweise Lösung diese Problems lässt sich erreichen, wenn für jeden Datenpunkt ein eigenes Secret verwendet und aus den resultierenden Hashes ein Merkle-Tree gebaut wird. Dessen Roothash (Top hash) wird dann in der Blockchain gespeichert. Hier das Erstellen eines Merkle-Trees für den Datensatz eines Kunden (wieder in JavaScript):

const web3 = require('web3')

let secretSize = 64

//Kundendaten
let name = 'Max'
let lastname = 'Mustermann'
let address = 'Musterstraße 42, 04321 Musterstadt'
let phone = '01577123456'

//Generierung der Secrets für alle Datenpunkte
let nameSecret = web3.utils.randomHex(secretSize)
let lastnameSecret = web3.utils.randomHex(secretSize)
let addressSecret = web3.utils.randomHex(secretSize)
let phoneSecret = web3.utils.randomHex(secretSize)

//Gernerirung der Merkle-Tree Leafs für alle Datenpunkte
let nameLeaf = web3.utils.sha3(nameSecret + name)
let lastnameLeaf = web3.utils.sha3(lastnameSecret + lastname)
let addressLeaf = web3.utils.sha3(addressSecret + address)
let phoneLeaf = web3.utils.sha3(phoneSecret + phone)

//Generierung des ersten Levels des Merkle-Trees
let aNode = web3.utils.sha3(nameLeaf + lastnameLeaf)
let bNode = web3.utils.sha3(addressLeaf + phoneLeaf)

//Generierung des Roothash des Merkle-Trees
let rootHash = web3.utils.sha3(aNode + bNode)
Wikipedia
Beispiel für einen Merkle-Tree (Abb. 1) (Bild: Wikipedia)

Um nun die Zugehörigkeit eines Hashes zu einem Kunden zu beweisen, wird einfach ein Merkle-Proof für das Leaf des betreffenden Datenpunkts generiert und mitsamt dessen Secret übermittelt. Das sei anhand eines Beispiels für das Erstellen eines Merkle-Proofs über die Telefonnummer eines Kunden in JavaScript verdeutlicht:

let proofText = '{"type":"Phone",' + '"secret":"' + phoneSecret + '","leaf":"' + addressLeaf + '","node":"' + aNode + '","root":"' + rootHash + '"}'

Voraussetzung für diesen Schritt sind die Daten aus dem dritten größeren Listing ("Hash mit Secret in JS"). Hier werden die dort gesetzten Variablen direkt verwendet, im Betrieb eine Datenbank. Es müssen übrigens nicht alle Daten zwischengespeichert werden. Da die Hashfunktion deterministisch ist, lassen sich Leafs und Nodes neu generieren, das heißt, es müssen lediglich die Daten, Secrets und der Roothash (zum einfachen Zuordnen) gespeichert werden.

Der Empfänger kann dann mit den ihm bekannten persönlichen Daten den Proof verifizieren und für sich bestätigen, dass der entsprechende Roothash tatsächlich in der Blockchain gespeichert ist:

//Voraussetzung ist der proofText aus dem vorhergehenden größeren Listing

const web3 = require('web3')


//Dem Verifizierer bekannter Datensatz
let verifyPhone = '01577123456'

//Parst den proofText als JavaScript-Objekt
let proof = JSON.parse(proofText)

//Generiert das zum bekannten Datensatz gehörige Leaf
let leaf = web3.utils.soliditySha3(verifyPhone + proof.secret)
let node
let root
//Switch zum korrekten Generieren des zugehörigen Roothashes
switch (proof.type) {
case 'Name':
node = web3.utils.sha3(leaf + proof.leaf)
root = web3.utils.sha3(node + proof.node)
break
case 'Lastname':
node = web3.utils.sha3(proof.leaf + leaf)
root = web3.utils.sha3(node + proof.node)
break
case 'Mail':
node = web3.utils.sha3(leaf + proof.leaf)
root = web3.utils.sha3(proof.node + node)
break
case 'Phone':
//Generiert die Node über dem zu verifizierenden Datensatz
node = web3.utils.sha3(proof.leaf + leaf)
//Generiert den Roothash aus eben generierter und übermittelter Node
root = web3.utils.sha3(proof.node + node)
break
}
//Überprüft, ob Proof valide ist, anschließend kann der Status des Roothashes in der Blockchain überprüft werden
return root === proof.rootHash

Ein Dritter kann nun im besten Fall den einen Datenpunkt einfacher erraten. Der Vorteil hierbei ist, dass sich mit den meisten einzelnen Datenpunkten kein sicherer Rückschluss auf die Identität der Person ziehen lässt – ein Vor- oder Nachname ist ja nicht einzigartig.

Abrunden lässt sich das Ganze durch sogenannte Zero-Knowledge-Proofs. Mit einem geeigneten Konstrukt lässt sich auf die Freigabe des Secrets verzichten. In der einfachsten Version hinterlegt man einfach einen Beweis, dass ein bestimmter öffentlicher Inputwert (z. B. Name der Person) Teil der Merkleroot ist. Das entsprechende Secret bleibt privat. Wenn nun jemand wissen will, ob er wirklich durch einen bestimmten Hash repräsentiert wird, kann er den Proof mit dem ihm bekannten Input (z. B. sein eigener Name) verifizieren.

Interessanterweise wird es dadurch für einen Angreifer wieder einfacher, die Hashes zu erraten, da er das Secret nicht mehr braucht. Er kann einfach einen Proof nehmen, alle möglichen Werte (z. B. Namen) als Input probieren, bis der Proof verifizierbar ist. Möglich wäre es nun, auf den Merkle-Tree zu verzichten und einfach alle Daten mit einem Secret (was notwendig bleibt, da sonst gleiche Personen immer gleiche Hashes haben werden, was zu einer einfachen Verfolgbarkeit führt) zu hashen. Das heißt, der öffentliche Input des Proofs ist nun die Gesamtheit der verwalteten personenbezogenen Daten, zum Beispiel Name, Nachname, Adresse und Telefonnummer. Das macht ein Erraten beziehungsweise Durchprobieren verschiedener Inputs wieder deutlich schwieriger.

Es ist auch möglich, auf andere Arten an die Daten zu kommen und dann den Proof zu nutzen, um herauszufinden, wer wer ist. Merkle-Tree und zk-SNARKs sind also ein Tradeoff, und es muss genau abgewogen werden, welcher Weg Verwendung finden soll.

Fazit

Mit dem hier beschriebenen Konstrukt können zwar keine personenbezogenen Daten direkt auf die Blockchain gespeichert werden, aber es können, bei richtiger Anwendung, trotzdem einige Vorteile der Blockchain genutzt werden. Die aktuell sehr strikte Formulierung von Artikel 17 ("Recht auf Vergessen") lässt wahrscheinlich ohnehin keine direkte Speicherung von personenbezogenen Daten auf Blockchain zu. Momentan sieht es so aus, dass der hier beschriebene Ansatz, nur eine Referenz (Hash) zu speichern und die eigentlichen Daten auf herkömmliche Weise in lokalen Datenbanken abzulegen, der einzige wirklich wasserdichte ist. Denn der Hash alleine kann nicht zur Wiederherstellung der Daten genutzt werden.

Durch das Vermeiden der Speicherung werden sowohl Artikel 16 als auch 17 bedient. Durch das zusätzlich verwendete Secret, das entweder durch den Merkle-Tree oder die zk-SNARKS eine zusätzliche Stärkung erfährt, wird auch Artikel 32 Genüge getan, sofern in den Backendsystemen für ausreichende Sicherheit gesorgt wird. (ane [1])

Maximilian Niemzik
ist Blockchain Engineer bei MaibornWolff.


URL dieses Artikels:
http://www.heise.de/-4337924

Links in diesem Artikel:
[1] mailto:ane@heise.de