Ende-zu-Ende Verschlüsselung

Traditionell wird der Content der Webseite in der Datenbank gespeichert und kann vom Server in Klartext gelesen werden. Im Falle eines Serverhacks, bei dem die Datenbank ausgelesen werden kann, kann der Angreifer alle Inhalte aus der Datenbank abgreifen und lesen.

Traditionelle Webseitenarchitektur

Wird Ende zu Ende verschlüsselt, landet der Inhalt selbst auch verschlüsselt in der Datenbank. Im Falle eines Angriffs und Daten-Highjacking sind die Daten verschlüsselt und für den Angreifer nutzlos.

Ende zu Ende – verschlüsselt

Glücklicherweise ist die Web-Cryptography mittlerweile in allen gängigen Browsern nativ implementiert, sodass man diese nutzen kann.

Table of Contents

Upload

  1. Zufälligen Schlüssel generieren mit dem der Inhalt verschlüsselt werden soll
const key = await window.crypto.subtle.generateKey(
  { name: "AES-GCM", length: 128 },
  true, // extractable
  ["encrypt", "decrypt"],
);Code-Sprache: JavaScript (javascript)

2. Inhalt mit Schlüssel verschlüsseln

const encrypted = await window.crypto.subtle.encrypt(
  { name: "AES-GCM", iv: new Uint8Array(12) /* don't reuse key! */ },
  key,
  new TextEncoder().encode(JSON.stringify(content)),
);Code-Sprache: JavaScript (javascript)

3. Verschlüsselten Inhalt auf den Server hochladen

const response = await (
  await fetch("/upload", {
    method: "POST",
    body: encrypted,
  })
).json();Code-Sprache: JavaScript (javascript)

4. Eine URL zum teilen des Inhaltes generieren (optional)

const objectURL = response.url;
const objectKey = (await window.crypto.subtle.exportKey("jwk", key)).k;
const url = objectURL + "#key=" + objectKey;Code-Sprache: JavaScript (javascript)

Download

Die andere Seite ist der Download der Datei zurück vom Server

const response = await fetch(`/download?id={id}`);
const encrypted = await response.arrayBuffer();Code-Sprache: JavaScript (javascript)

Der Schlüssel den wir kodiert in der URL im k-Feld des jwk-Objektes erhalten haben, stellt den eigentlichen Schlüssel dar. Um das Schlüsselobjekt zurück zu erhalten müssen wir dieses mit all seinen Feldern reproduzieren.

const objectKey = window.location.hash.slice("#key=".length);
const key = await window.crypto.subtle.importKey(
  "jwk",
  {
    k: objectKey,
    alg: "A128GCM",
    ext: true,
    key_ops: ["encrypt", "decrypt"],
    kty: "oct",
  },
  { name: "AES-GCM", length: 128 },
  false, // extractable
  ["decrypt"],
);Code-Sprache: JavaScript (javascript)

Wir entschlüsseln die Nachricht zurück zu einem String und konvertieren sie zurück in den ursprünglichen JSON-String

const decrypted = await window.crypto.subtle.decrypt(
  { name: "AES-GCM", iv: new Uint8Array(12) },
  key,
  encrypted,
);
const decoded = new window.TextDecoder().decode(new Uint8Array(decrypted));
const content = JSON.parse(decoded);Code-Sprache: JavaScript (javascript)

Quelle: Excalidraw