--- title: Rotación de claves icon: rotate --- Twenty tiene dos familias de claves independientes: * **Claves de firma JWT** — pares de claves asimétricas ES256 (etiquetados con `kid`) almacenados en `core."signingKey"`, utilizados para firmar y verificar tokens de acceso/renovación. * **Clave de cifrado en reposo** — `ENCRYPTION_KEY`, se usa para cifrar tokens OAuth, variables de la aplicación, claves privadas de las claves de firma, valores de configuración confidenciales y secretos TOTP dentro de un sobre `enc:v2:`. `APP_SECRET` es un secreto heredado mantenido para compatibilidad retroactiva: cuando `ENCRYPTION_KEY` no está definido actúa como el cifrado en reposo / alternativa para la cookie de sesión, y todavía verifica los tokens de acceso HS256 preexistentes. Quedará obsoleto. ## Claves de firma JWT Cada clave contiene una `publicKey` (conservada indefinidamente para poder verificar los tokens emitidos anteriormente), una `privateKey` cifrada (usada solo mientras la clave sea la actual), un indicador `isCurrent` (exactamente una fila a la vez) y un `revokedAt` opcional. ### Rotar la clave actual Establece `SIGNING_KEY_ROTATION_DAYS` para habilitarlo: un cron diario emite una nueva clave actual cuando la existente es más antigua que ese umbral. Las claves anteriores *no* se revocan, por lo que los tokens firmados con ellas siguen verificándose. Deja la variable sin configurar para desactivar la rotación automática. La rotación automática se incluye a partir de la versión v2.6+. ### Revocar una clave (solo en caso de filtración / emergencia) **Settings → Admin Panel → Signing keys → Revoke** en una fila que no sea la actual. Borra el material privado cifrado, establece `revokedAt` y rechaza todos los tokens existentes firmados con ese `kid`. ## Rotar `ENCRYPTION_KEY` El comando `secret-encryption:rotate` descrito a continuación se incluye a partir de la versión v2.6+. Cada valor cifrado se encapsula como `enc:v2:\:\`, donde `\` es un prefijo hexadecimal de 8 caracteres derivado de la clave en bruto. La rotación es en línea y reanudable. 1. **Generar una nueva clave**: `openssl rand -base64 32`. 2. **Configurar ambas claves en paralelo** en `.env`, luego reinicie: ```ini ENCRYPTION_KEY=NEW_VALUE FALLBACK_ENCRYPTION_KEY=OLD_VALUE ``` Las nuevas escrituras usan la nueva clave, las filas existentes todavía se descifran mediante la clave de respaldo. 3. **Volver a cifrar las filas existentes**: ```bash docker exec -it {server_container} yarn command:prod secret-encryption:rotate ``` El comando recorre seis sitios (`connected-account-tokens`, `application-variable`, `application-registration-variable`, `signing-key-private-keys`, `sensitive-config-storage`, `totp-secrets`). Un filtro SQL omite las filas que ya están en el nuevo `\`, por lo que el comando es idempotente: interrúmpalo y vuelva a ejecutarlo según sea necesario. Finaliza con código distinto de cero si falla alguna fila; vuelva a ejecutar para reintentar. | Opción | Descripción | | ---------------------------------------- | ----------------------------------------------------------- | | `-s, --site \` | Limitar a un solo sitio. | | `-b, --batch-size \` | Filas por lote (predeterminado `200`, máximo `5000`). | | `-d, --dry-run` | Descifrar + volver a cifrar en memoria, omitir el `UPDATE`. | 4. **Elimine la clave de respaldo** una vez que `--dry-run` muestre cero filas restantes: quite `FALLBACK_ENCRYPTION_KEY` y reinicie. ## Compatibilidad heredada con `APP_SECRET` Las instancias más antiguas que nunca configuraron `ENCRYPTION_KEY` usan `APP_SECRET` como la clave de cifrado en reposo (y como el secreto de la cookie de sesión, derivado de esta). Esta ruta se conserva por compatibilidad retroactiva, pero está **en desuso**: configure una `ENCRYPTION_KEY` dedicada y siga el procedimiento de rotación anterior para migrar y dejar de usarla. El propio `APP_SECRET` sigue utilizándose para verificar tokens de acceso HS256 heredados.