Verschlüsselung
Object-Storage-Payloads unterstützen Client-Side-AES-GCM-Verschlüsselung — das Framework verschlüsselt vor dem Put, entschlüsselt beim Get, Schlüssel werden über einen MasterKeyRing verwaltet.
import { ObjectStorageDurableStateStore, S3ObjectStorageBackend, MasterKeyRing,} from 'actor-ts';
const keyRing = new MasterKeyRing({ keys: { 'k1': process.env.MASTER_KEY_V1! }, currentKeyId: 'k1',});
const store = new ObjectStorageDurableStateStore({ backend: new S3ObjectStorageBackend({ /* ... */ }), encryption: { keyRing },});Jetzt wird jeder persistierte State vor dem Upload mit AES-GCM
unter k1 verschlüsselt. Reads entschlüsseln transparent.
Warum Client-Side-Verschlüsselung
Abschnitt betitelt „Warum Client-Side-Verschlüsselung“Object Stores (S3, GCS, Azure Blob) bieten eingebaute Server-Side-Encryption. Warum auch Client-Side verschlüsseln?
| Bedrohung | Server-Side | Client-Side |
|---|---|---|
| Storage-Kompromittierung (jemand liest von der Disk) | ✓ | ✓ |
| Account-Kompromittierung (jemand hat S3-Credentials) | ✗ | ✓ |
| Cloud-Provider-Kompromittierung | ✗ | ✓ |
| Audit / Compliance, die “wir halten die Schlüssel” verlangt | ✗ | ✓ |
Client-Side-Verschlüsselung schützt vor mehr Bedrohungen, kostet aber mehr (CPU pro Op, Schlüsselverwaltungs-Overhead). Die meisten Apps sollten beides verwenden: Server-Side als Baseline + Client-Side für sensible Payloads.
Konfiguration
Abschnitt betitelt „Konfiguration“interface EncryptionConfig { keyRing: MasterKeyRing; /** Den Algorithmus überschreiben; Default 'AES-GCM'. */ algorithm?: 'AES-GCM';}Der MasterKeyRing trägt:
keys— keyId → Schlüssel-Bytes-Map.currentKeyId— welchen Schlüssel neue Writes verwenden.
Mehrere Schlüssel im Ring lassen dich ältere Payloads (unter älteren Schlüsseln verschlüsselt) lesen, während neue Payloads den aktuellen Schlüssel verwenden. Das ist das Fundament der Schlüsselrotation.
Wie es funktioniert
Abschnitt betitelt „Wie es funktioniert“Beim Put: Wert serialisieren → Bytes → AES-GCM(bytes, currentKey) → Ciphertext → Metadaten { keyId: currentKeyId, iv, tag } → S3.put(ciphertext, x-amz-meta-key-id: currentKeyId, etc.)
Beim Get: S3.get → Ciphertext + Metadaten{ keyId, iv, tag } → keyRing.get(keyId) → decrypt(ciphertext, key, iv, tag) → Bytes → deserialisierenDie Key-ID wird neben dem Ciphertext gespeichert — jeder Blob weiß, mit welchem Schlüssel er verschlüsselt wurde. Das lässt das Framework alte Payloads mit dem richtigen Schlüssel entschlüsseln, auch nachdem der aktuelle Schlüssel rotiert wurde.
Was es verschlüsselt
Abschnitt betitelt „Was es verschlüsselt“- Den Body — serialisierter State / Event / Snapshot.
- Nicht — den Object-Key, Metadaten-Header (außer der Key-ID), den Bucket-Namen.
Für Object-Metadaten, die nicht durchsickern sollten (sensible persistenceIds), verwende ein separates Naming-Schema (hashe IDs, bevor sie zu Object-Keys werden).
Schlüsselquellen
Abschnitt betitelt „Schlüsselquellen“Die Master-Key-Bytes kommen irgendwoher. Häufige Muster:
Env-Vars
Abschnitt betitelt „Env-Vars“new MasterKeyRing({ keys: { 'k1': Buffer.from(process.env.MASTER_KEY_V1!, 'base64'), }, currentKeyId: 'k1',});Am einfachsten. Jeder Schlüssel ist ein 32-Byte-Buffer (für AES-256-GCM), base64-kodiert in der Env.
Risiko: Env-Vars sind für alles sichtbar, das die Prozess-Umgebung lesen kann. Verwende nur, wenn die Env selbst gesichert ist (K8s-Secrets, etc.).
KMS-on-Load
Abschnitt betitelt „KMS-on-Load“import { KMS } from '@aws-sdk/client-kms';
const kms = new KMS();const decrypted = await kms.decrypt({ KeyId: 'alias/master', CiphertextBlob: Buffer.from(process.env.WRAPPED_KEY!, 'base64'),});
const keyRing = new MasterKeyRing({ keys: { 'k1': decrypted.Plaintext! }, currentKeyId: 'k1',});Der Master-Key wird verschlüsselt unter einem Cloud-KMS-Key gespeichert. Die App holt ihn beim Start, entschlüsselt über KMS, hält ihn im Speicher.
Besser als rohe Env-Vars — nur KMS-Zugriff ist erforderlich, um Schlüssel wiederherzustellen.
HashiCorp Vault
Abschnitt betitelt „HashiCorp Vault“Ähnliches Muster: Master-Keys beim Start aus Vault ziehen.
CPU-Kosten
Abschnitt betitelt „CPU-Kosten“AES-GCM ist schnell — moderne CPUs haben Hardware-Unterstützung.
Pro 100 KB Verschlüsseln + Entschlüsseln:
- ~0,5-1 ms auf modernem x86 / Apple Silicon.
- Auf kleinen Objekten effektiv kostenlos.
Für die meisten Workloads ist Verschlüsselung in Profilen unsichtbar.
Verschlüsselung + Kompression
Abschnitt betitelt „Verschlüsselung + Kompression“verschlüsseln → komprimieren → S3.put # ✗ kein Kompressionsnutzen auf Ciphertextkomprimieren → verschlüsseln → S3.put # ✓ das macht das FrameworkDie Reihenfolge des Frameworks ist zuerst komprimieren, dann verschlüsseln — komprimierte Bytes sind immer noch komprimierbar (nicht zufällig); nach der Verschlüsselung sind sie effektiv zufällig und unkomprimierbar.
Wenn du sowohl Kompression als auch Verschlüsselung setzt, bekommst du diese Reihenfolge automatisch.
Alte Payloads lesen
Abschnitt betitelt „Alte Payloads lesen“Nachdem du Verschlüsselung auf einem zuvor unverschlüsselten Bucket aktiviert hast:
state/cart-42 ← alt, Plaintext, keine Key-ID-Metadatenstate/cart-43 ← neu, verschlüsselt, Key-ID: k1Das Framework erkennt jedes Pro-Payload über die Metadaten:
- Keine Key-ID-Metadaten → Plaintext-Pfad.
- Key-ID vorhanden → über den keyRing entschlüsseln.
Bedeutet, du kannst Verschlüsselung schrittweise aktivieren — neue Writes werden verschlüsselt, alte Reads funktionieren immer noch, und ein Hintergrund-Re-Encryption-Sweep kann den Rest migrieren.
Siehe Schlüsselrotation für den Rotation-Flow.
Wie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Object Storage im Überblick — das größere Bild.
- Schlüsselrotation — der Online-Rotations-Flow.
- Master-Key-Rotation (Operations) — die operative Seite.
- Per-Actor-Policies — Per-Actor-Verschlüsselungskonfiguration.