CodamAIDocs
Themafertig

Verschlüsselte Felder

Ein Textfeld mit der Regel „verschlüsselt“ steht verschlüsselt in der Datenbank und kommt entschlüsselt zurück. Was du dafür einstellst, wie ein Schlüsselwechsel geht und was mit dem Feld nicht geht.

Ausprägungen
Anlegen und Ändern: verschlüsselt speichernLesen, Suchen, Historie: entschlüsselt ausliefernSchlüssel und frühere SchlüsselWerte, die noch im Klartext stehenFilter und Sortierung abgelehntnicht zusammen mit „eindeutig“

Worum es geht

Manche Werte sollen nicht lesbar in der Datenbank stehen, etwa eine IBAN oder eine Steuernummer. Wer die Datenbank oder eine Sicherung in die Hände bekommt, soll damit nichts anfangen können.

Dafür setzt du am Textfeld im Hub die Regel verschlüsselt (@encrypted). CDMS verschlüsselt den Wert dann vor dem Speichern mit AES-256-GCM und entschlüsselt ihn beim Lesen. Der Client merkt davon nichts: Er schickt Klartext und bekommt Klartext.

Der Weg eines Wertes

sequenceDiagram
    participant C as Client
    participant D as CDMS
    participant DB as Datenbank
    C->>D: POST iban = "DE44 5001 …"
    D->>D: Regeln prüfen (am Klartext)
    D->>D: verschlüsseln, neuer Zufalls-IV
    D->>DB: speichert "enc:v1:fdd5117f:InOkKngP…"
    D->>DB: liest das Objekt für die Antwort
    D->>D: entschlüsseln
    D-->>C: 200, iban = "DE44 5001 …"

Die gespeicherte Form beginnt immer mit enc:v1:, dann folgt die Kennung des Schlüssels, dann der Geheimtext. Jede Verschlüsselung nimmt einen neuen Zufallswert (IV). Zwei gleiche IBANs stehen deshalb verschieden in der Datenbank.

Das Verschlüsseln erledigt ein mitgelieferter Feld-Hook: EncryptedFieldHook aus dem Modul cdms-field-encryption. Du schreibst dafür keinen Code. Der Generator hängt den Hook an jedes Feld mit der Regel, und der Projektrahmen nimmt das Modul in die pom.xml auf. Hat das Feld noch eigene Feld-Hooks, sehen diese den Klartext: Verschlüsselt wird als letzter Schritt vor der Datenbank.

Den Schlüssel einstellen

Die Anwendung braucht einen Schlüssel aus 32 Zufallsbytes, in Base64 geschrieben:

openssl rand -base64 32
UmgebungsvariablePropertyBedeutung
CODAMAI_CDMS_ENCRYPTION_KEYcodamai.cdms.encryption.keyder aktive Schlüssel, mit dem jeder Wert geschrieben wird
CODAMAI_CDMS_ENCRYPTION_PREVIOUS_KEYScodamai.cdms.encryption.previous-keysfrühere Schlüssel, kommagetrennt, nur zum Lesen

Der Schlüssel gehört in ein Secret deiner Umgebung, nie ins Modell und nie ins Repository. Nur eine Anwendung, in der ein Feld verschlüsselt ist, braucht ihn. Dort startet sie ohne ihn nicht:

field-encryption-key-missing|codamai.cdms.encryption.key: a model encrypts a field (@encrypted), …

Ein Schlüssel, der nicht 32 Bytes lang ist, stoppt den Start ebenso (field-encryption-key-invalid).

Den Schlüssel wechseln

  1. 1
    Betrieb
    erzeugt einen neuen Schlüssel
  2. 2
    Betrieb
    setzt ihn als CODAMAI_CDMS_ENCRYPTION_KEY und schiebt den alten nach CODAMAI_CDMS_ENCRYPTION_PREVIOUS_KEYS
  3. 3
    CDMS
    liest alte Werte mit dem alten Schlüssel, erkennbar an seiner Kennung im gespeicherten Wert
  4. 4
    CDMS
    verschlüsselt jeden Wert, der neu geschrieben wird, mit dem neuen Schlüssel

Einen alten Schlüssel entfernst du erst, wenn kein Wert mehr mit ihm gespeichert ist. Fehlt der Schlüssel eines gespeicherten Wertes, liefert CDMS ihn nicht aus, sondern antwortet mit 500 field-encryption-key-unknown|<Kennung>.

Werte, die noch im Klartext stehen

Stellst du ein Feld nachträglich auf verschlüsselt, stehen seine bisherigen Werte noch im Klartext in der Datenbank. CDMS erkennt sie am fehlenden enc:v1: und liefert sie unverändert aus. Beim nächsten Schreiben des Feldes wird der Wert verschlüsselt. Ein Schreibvorgang, der das Feld nicht sendet, etwa ein PATCH auf ein anderes Feld, lässt ihn, wie er ist.

Was mit einem verschlüsselten Feld nicht geht

Geht das mit einem verschlüsselten Feld?
VorhabenAntwort
das Feld lesen, auch in einer Liste oder in der Historieja, entschlüsselt
das Feld schreiben (POST, PUT, PATCH)ja, CDMS verschlüsselt
nach dem Feld filtern400 field-not-searchable|<Feld>
nach dem Feld sortieren400 field-not-searchable|<Feld>
das Feld zusätzlich „eindeutig“ machengeht nicht: der Hub lehnt es ab, der Generator bricht ab
ein Zahlen- oder Datumsfeld verschlüsselngeht nicht: die Regel gibt es nur an Textfeldern

Filter und Sortierung arbeiten in der Datenbank, also mit der gespeicherten Form. Die sieht für jeden Wert anders aus. Ein Filter fände deshalb nie etwas, eine Sortierung ergäbe Zufall. Statt falsch zu antworten, lehnt CDMS beides ab. Aus demselben Grund kann die Datenbank keine Eindeutigkeit prüfen.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-field-encryption – EncryptedFieldHook (AES/GCM, MARKER, KEY, PREVIOUS_KEYS, storedLength)
  • CDMS/cdms-generator – DtoMetaProcessor.fieldHooks (ENCRYPTION_HOOK), EntityProcessor (columnLength, isLob)
  • CDMS/cdms-scaffold – CdmsSystemContext (detectEncryptedFields), cdms-version-registry.yaml (encryptionDependencies), CdmsScaffoldService (.env.example)
  • CDMS/cdms-system-layer – SimpleFieldRoles.assertSearchable, HookManagementSystem (isSearchable, callFieldHooksAfterRead), AbstractLayer.queryHistory
  • CDMS/frontend – shared/utils/cdmsFieldRules (ENCRYPTED_RULE, fieldRulesError)
  • CDMS/cdms-integrationtest – AbstractEncryptedFieldTest, Modell secret (iban)
  • CDMS/cdms-system-layer/docs/adr – ADR-018
  • documentation/40-sicherheit/05-feldverschluesselung.md
Suchen