Verschlüsseln im Browser – die WebCrypto API

Sichere Kommunikation und insbesondere Ende-zu-Ende Verschlüsselung werden für Web- und Mobile-Dienste immer wichtiger. Die IT-Architekturen moderner Lösungen zeichnen sich zunehmend durch Aspekte der Dezentralität aus, sodass Kryptografie sich nicht nur auf der Serverseite, sondern zunehmend auch im Browser, d.h. auf der Clientseite abspielt. Hier kommt die WebCrypto API ins Spiel. Das ist eine W3C Recommendation, die es Javascript-Applikationen erlaubt, kryptografische Dienste plattformübergreifend zu nutzen – kurz: der Browser kann crypto. Das erste Release der Recommendation stammt aus dem Jahr 2017, und für einen langen Zeitraum hatten viele Browser eine leider nur unzufriedenstellende Abdeckung der Möglichkeiten. Dies hat sich aber mittlerweile gebessert – Zeit, sich das ganze mal genauer anzuschauen.

Was genau meint WebCrypto?

Die WebCrypto API ist ein Low-Level Interface zu Browser-Implementierungen von kryptografischen Operationen. Entwickler:innen können damit zahlreiche Funktionen rund um Kryptografie einsetzen, um Applikationen im Browser und im Backend sicherer zu gestalten. Dabei werden durch die Browser verschiedene kryptografische Primitive umgesetzt, aus denen sich komplexere kryptografische Anwendungsfälle zusammensetzen lassen. Mit der WebCrypto API lassen sich

  • Hashes wie z. B. SHA-256 anbringen,
  • Symmetrische Schlüssel und asymmetrische Schlüsselpaare erzeugen, z.B. für AES oder ECDSA,
  • Schlüssel voneinander ableiten, z. B. einen symmetrischen Schlüssel aus einem Master-Passwort mit Hilfe von PBKDF2,
  • Daten ver- und entschlüsseln (z. B. symmetrisch mit AES),
  • Daten signieren und Signaturen prüfen, z. B. mit ECDSA oder HMAC,
  • auf sichere Art Schlüssel vereinbaren, z. B. mit ECDH.

Weiterhin ist Funktionalität enthalten, um kryptografisch starke Zufallsdaten mit hoher Entropie zu erzeugen. Die obige Liste steckt voll von Abkürzungen aus der Krypto-Welt, deshalb eine kurze Einordung:

  • SHA ist der Secure Hash Algorithm, der Standard-Algorithmus für das Erstellen eindeutiger Fingerprints (den Hashes) aus beliebigen Daten.
  • Ein Hash kann zusätzlich einen symmetrischen Schlüssel in die Berechnung einfließen lassen. Dann ist er kryptografisch stark und spricht man von einem keyed-hash message authentication code, „HMAC“.
  • AES, der Advanced Encryption Standard, ist der weitverbreitete Standard für symmetrische Verschlüsselung in verschiedenen Modi, für Blockchiffren (immer ein Block aus X Bytes) und Stromchiffren (Byte für Byte).
  • Immer wenn Abkürzung mit „EC“ beginnen, ist von Elliptischen Kurven die Rede – einem mathematischen Verfahren, das zur Konstruktion sicherer Verschlüsselungsmethoden verwendet wird. Hier ist mit ECDSA der EC-basierte Digital Signature Algorithm relevant sowie ECDH für eine Diffie-Hellmann-basierten Schlüsselaustausch.

Wie lässt sich dies nun konkret umsetzen?

Bisher war das Thema Kryptografie eher eines, was auf Seiten der Server lokalisiert war. Ein Frontend macht Zugriffe per HTTPS, aber komplexere Verschlüsselungs- und Signaturverfahren waren Sache der Applikations-Backends. Ein Teil der Begründung ist sicherlich die bessere Verfügbarkeit von Crypto-Bibliotheken in den backend-artigen Sprachen wie Java, Go, C#. Neben Encryption-in-transit ist Encryption-at-rest aber auch einfach ein Backend-Thema, da dort die Datenbanken angesiedelt sind.

Mit so einer Architektur ist es aber praktisch nicht möglich, dass Nutzer:innen die IT-Sicherheit „in die eigene Hand nehmen“, da sie nicht im Besitz der privaten Schlüssel sind und architekturbedingt auch nicht sein können, wenn die Geschäftslogik im Backend angesiedelt ist.

Mit Einzug modernerer, dezentralisierter und browser-basierter Applikationen ändert sich das nun. Eine echte Ende-zu-Ende-Sicherheit wird erst dann möglich, wenn private Schlüssel auf dem Client erzeugt werden, auch dort verbleiben, und Nachrichten bzw. Content direkt im Browser ver- und entschlüsselt wird. Die Kommunikation kann über Server verlaufen, diese sind aber mangels privater Schlüssel nicht in der Lage zu entschlüsseln.

Genauso ist eine Web3-Architektur ohne client-seitige Kryptografie, wie in den Browser-Plugins der Crypto-Wallets, nicht denkbar. Gute Gründe für Crypto im Browser, aber was sind nun genau die Anwendungsfälle?

Was sind Anwendungsfälle für WebCrypto?

Aus zahlreichen Anwendungsfällen schauen wir uns drei genauer an: Sichere Datenspeicherung über die WebStorage API, die besagte Ende-zu-Ende-Verschlüsselung im Browser und das Signieren von Daten als digitale Unterschrift im Kleinen.
Die WebStorage API erlaubt es Daten im Browser lokal zu speichern, entweder im Session Storage für die aktuelle Seitenansicht oder permanent im Local Storage. Ein Nachteil aus Sicht der IT-Sicherheit ist, dass die Speicherorte der Browser für diese Daten bekannt sind, d.h. bei kompromittierten Systemen – etwa durch Trojaner – können diese Informationen abgefischt werden. Best Practise ist nun, sicherheitsrelevante Daten nicht unverschlüsselt über die WebStorage API abzulegen, sondern innerhalb des DOM oder im Cookie-Store zu halten. Eine Möglichkeit besteht nun darin, ein Master-Kennwort im Backend zu speichern und dem Client nach erfolgreicher Authentifizierung auszuliefern. Der Client leitet daraus geeignet einen symmetrischen Schlüssel ab, verschlüsselt die kritischen Daten und legt sie dann über die WebStorage API ab.

Die Ende-zu-Ende Verschlüsselung wurde oben bereits erwähnt und ist z. B. aus Messenger-Apps wie WhatsApp oder optional in Telegram bekannt. Dies lässt sich lösen, in dem zwei Kommunikationspartner unabhängig voneinander ein asymmetrisches Schlüsselpaar, z.B. mit Hilfe Elliptischer Kurven, erzeugen. Beide tauschen dann jeweils den öffentlichen Schlüssel aus. Aus dem eigenen privaten Schlüssel und dem öffentlichen Schlüssel der Gegenseite errechnen beide über ein Diffie-Hellman-Verfahren denselben symmetrischen Schlüssel – und verschlüsseln ihre Kommunikation damit. Ein Außenstehender kann nur aus den öffentlichen Schlüsseln nicht den symmetrischen Schlüssel berechnen.

Das kryptografische Signieren von Dokumenten wie PDF-Dateien ist bekannt. Schlüssel und Zertifikate liegen dabei meist im Zertifikatsspeicher des Betriebssystems, und es werden passende Werkzeuge wie Adobe Acrobat notwendig. Mit der WebCrypto API ist es auch möglich, beliebig strukturierte Daten zu signieren und so zu zeigen, dass die Daten „aus der eigenen Feder“ stammen. Zum Beispiel könnte ein(e) Nutzer:in in einer Browser-App ein Schlüsselpaar erzeugen und über die Native Filesystem API lokal auf der Festplatte speichern. Bei nächster Nutzung der App muss das Schlüssel dann wieder geladen werden, und kann dann verwendet werden, um Formulardaten kryptografisch zu Signieren. Der Server erhält nur den öffentlichen Teil des Schlüssels zur Weiterverteilung an andere Clients, die damit dann die Signatur prüfen können.

Im Code …

Die WebCrypto-API hat einen gewissen Umfang, aber einen kleinen Einblick in die Nutzung möchten wir schon noch geben und zum Ausprobieren anregen.
Die Schnittstelle zur API verläuft über das Object window.crypto und window.crypto.subtle. Um zu sehen, ob das Browser das ganze unterstützt, reicht:

kopieren
const crypto= window.crypto || window.msCrypto;
if (crypto.subtle !== undefined) {
console.log("WebCrypto API is ready.");
} else {
console.log("This browser does not support the webcrypt api the way we need it.");
}

Als erstes erzeugen wir einen Schlüssel, der für symmetrische AES-Verschlüsselung passt, mit einer Schlüssellänge von 256 Bit. Weiterhin können wir bei der Erzeugung mit angeben, welche kryptografischen Operationen möglich sein sollen:

kopieren
async function generateKey(cs) {
return new Promise( (resolve, reject) => {
cs.generateKey(
{
name: "AES-CBC",
length: 256,
},
false,
["encrypt", "decrypt"]
)
.then(function(key){
resolve(key);
})
.catch(function(err){
reject(err);
});

})
}


(async () => {

  try {

    // 1. generate a symmetric key suitable for symmetric AES

    const key = await generateKey(crypto.subtle);

    console.log(key);

Die Console verrät:

Das Property “Extractable” beschreibt dabei, ob man den Schlüssel aus der WebCrypto-Implementierung herausziehen darf, um ihn in Rohform zu verarbeiten.

Um etwas zu verschlüsseln, wählt man die encrypt-Funktion mit passender Parametrisierung, z.B. AES im Modus „CBC“. Welcher Modus gewählt wird hängt stark vom Anwendungsfall ab und sollte genau evaluiert werden. CBC arbeitet mit einem Initialisierungsvektor (IV), der je Kontext nur einmal verwendet werden darf, beim Ver- und Entschlüsseln aber identisch vorliegen muss.

kopieren
async function encryptWithIVAndKey(cs,iv,key,dt) {
return new Promise( (resolve, reject) => {
cs.encrypt(
{
name: "AES-CBC",
iv: iv,
},
key,
dt
)
.then(function (encrypted) {
resolve(new Uint8Array(encrypted));
})
.catch(function (err) {
reject(err);
});
});
}

Um die Funktion zu nutzen, erzeugen wir einen zufälligen IV und bauen aus einem Klartext-String ein ArrayBuffer auf. Zum Schluss geben wir das Verschlüsselte aus.

kopieren
// 2. encrypt something
// 2.1. create an initialization vector. Never re-use this.
const iv = window.crypto.getRandomValues(new Uint8Array(16));

// 2.2. encode a string to an arraybuffer and encrypt
const plain = new TextEncoder().encode("EncryptThisAndWeShallSee!")
const enc = await encryptWithIVAndKey(crypto.subtle,iv,key,plain);
console.log(new TextDecoder().decode(enc));

Ergibt im Beispiel:

Die Entschlüsselung läuft gleich mit passenden „decrypt“-Funktionen, identischem Schlüssel und IV:

kopieren
async function decryptWithIVAndKey(cs,iv,key,dt) {
return new Promise( (resolve, reject) => {
cs.decrypt(
{
name: "AES-CBC",
iv: iv,
},
key,
dt
)
.then(function (decrypted) {
resolve(new Uint8Array(decrypted));
})
.catch(function (err) {
reject(err);
});
});
}

Ähnlich lassen sich die anderen kryptografischen Funktionen verwenden. Das obige Beispiel dient nur zur Veranschaulichung der API – wo und wie Schlüssel und Initialisierungsvektor abgelegt werden ist abhängig vom Anwendungsfall.

Fazit

Die Browser-Unterstützung der WebCrypto API ist noch nicht vollumfänglich, die Mehrzahl der großen Browser unterstützt aber die relevanten kryptografischen Primitive und erlaubt es, sinnvolle Applikationen zu bauen. Bei neuen Entwicklungsvorhaben kann es sich lohnen, die Anforderungen und Anwendungsfälle daraufhin zu prüfen, ob Security- und Crypto-Anteile auch im Browser umsetzbar sind.

Seite teilen