---
title: Schlüsselrotation
icon: rotate
---

Twenty hat zwei unabhängige Schlüsselfamilien:

* **JWT-Signaturschlüssel** — asymmetrische ES256-Schlüsselpaare (`kid`-markiert), die in `core."signingKey"` gespeichert sind und zum Signieren und Verifizieren von Access-/Refresh-Tokens verwendet werden.
* **Verschlüsselungsschlüssel im Ruhezustand** — `ENCRYPTION_KEY`, wird verwendet, um OAuth-Tokens, Anwendungsvariablen, die privaten Schlüssel der Signierschlüssel, sensible Konfigurationswerte und TOTP-Geheimnisse innerhalb eines `enc:v2:`-Umschlags zu verschlüsseln.

`APP_SECRET` ist ein veraltetes Secret, das aus Gründen der Abwärtskompatibilität beibehalten wird: Wenn `ENCRYPTION_KEY` nicht gesetzt ist, fungiert es als Fallback für At-Rest-Verschlüsselung / Session-Cookie und verifiziert weiterhin bereits bestehende HS256-Access-Tokens. Es wird als veraltet markiert werden.

## JWT-Signierschlüssel

Jeder Schlüssel enthält einen `publicKey` (unbefristet aufbewahrt, damit er zuvor ausgestellte Tokens verifizieren kann), einen verschlüsselten `privateKey` (nur verwendet, solange der Schlüssel aktuell ist), ein `isCurrent`-Flag (zu jedem Zeitpunkt genau eine Zeile) und ein optionales `revokedAt`.

### Aktuellen Schlüssel rotieren

Setzen Sie `SIGNING_KEY_ROTATION_DAYS`, um die Funktion zu aktivieren: Ein täglicher Cronjob erstellt dann einen neuen aktuellen Schlüssel, sobald der bestehende Schlüssel älter als dieser Schwellenwert ist. Frühere Schlüssel werden *nicht* widerrufen, daher werden Tokens, die darunter signiert wurden, weiterhin verifiziert. Lassen Sie die Variable ungesetzt, um die automatische Rotation zu deaktivieren.

<Note>Die automatische Rotation ist ab Version v2.6 verfügbar.</Note>

### Einen Schlüssel widerrufen (nur bei Leak / Notfall)

**Settings → Admin Panel → Signing keys → Revoke** in einer nicht aktuellen Zeile. Löscht das verschlüsselte private Material, setzt `revokedAt` und weist jedes vorhandene Token zurück, das unter diesem `kid` signiert wurde.

## `ENCRYPTION_KEY` rotieren

<Note>Der unten beschriebene Befehl `secret-encryption:rotate` ist ab v2.6+ enthalten.</Note>

Jeder verschlüsselte Wert wird als `enc:v2:\<keyId>:\<payload>` verpackt, wobei `\<keyId>` ein 8-stelliges Hex-Präfix ist, das aus dem Rohschlüssel abgeleitet wird. Die Rotation erfolgt online und ist fortsetzbar.

1. **Einen neuen Schlüssel generieren**: `openssl rand -base64 32`.

2. **Beide Schlüssel nebeneinander konfigurieren** in `.env` und dann neu starten:
   ```ini
   ENCRYPTION_KEY=NEW_VALUE
   FALLBACK_ENCRYPTION_KEY=OLD_VALUE
   ```
   Neue Schreibvorgänge verwenden den neuen Schlüssel, vorhandene Zeilen werden weiterhin über den Fallback entschlüsselt.

3. **Vorhandene Zeilen erneut verschlüsseln**:

   ```bash
   docker exec -it {server_container} yarn command:prod secret-encryption:rotate
   ```

   Der Befehl durchläuft sechs Sites (`connected-account-tokens`, `application-variable`, `application-registration-variable`, `signing-key-private-keys`, `sensitive-config-storage`, `totp-secrets`). Ein SQL-Filter überspringt Zeilen, die bereits auf dem neuen `\<keyId>` sind, sodass der Befehl idempotent ist: Unterbrechen und bei Bedarf erneut ausführen. Beendet sich mit einem von Null verschiedenen Exit-Code, wenn irgendeine Zeile fehlschlägt — zum Wiederholen erneut ausführen.

   | Flag                                     | Beschreibung                                                                 |
   | ---------------------------------------- | ---------------------------------------------------------------------------- |
   | `-s, --site \<site>`    | Auf eine einzelne Site beschränken.                                          |
   | `-b, --batch-size \<n>` | Zeilen pro Batch (Standard `200`, Maximum `5000`).                           |
   | `-d, --dry-run`                          | Entschlüsseln + erneut im Speicher verschlüsseln, das `UPDATE` überspringen. |

4. **Den Fallback entfernen**, sobald `--dry-run` null verbleibende Zeilen anzeigt: `FALLBACK_ENCRYPTION_KEY` entfernen und neu starten.

## Legacy-Unterstützung für `APP_SECRET`

Ältere Instanzen, die niemals `ENCRYPTION_KEY` gesetzt haben, verwenden `APP_SECRET` als At-Rest-Verschlüsselungsschlüssel (und als Session-Cookie-Secret, das daraus abgeleitet wird). Dieser Pfad wird aus Gründen der Abwärtskompatibilität beibehalten, ist aber **veraltet** — setze einen dedizierten `ENCRYPTION_KEY` und folge dem oben beschriebenen Rotationsverfahren, um davon zu migrieren. `APP_SECRET` selbst bleibt in Verwendung, um Legacy-HS256-Access-Tokens zu verifizieren.
