Chiffrement

Chiffrement symétrique AES-256-GCM des secrets applicatifs stockés en base, tokens tiers, secrets de webhooks.

core/crypto chiffre les valeurs sensibles qui doivent être stockées en base et relues en clair : token d'API d'un service tiers, secret de webhook, identifiants d'un connecteur. Ce sont les cas où un hachage à sens unique ne convient pas parce qu'il faut retrouver la valeur d'origine.

Pour un mot de passe utilisateur, ne pas utiliser ce module : core/users hache avec hashPassword.

Import

import { isEncryptionAvailable, encryptSecret, decryptSecret } from '@lrvo/core/crypto';

Module serveur : il lit LRVO_ENCRYPTION_KEY et utilise node:crypto.

Variables d'environnement

VariableObligatoireRôle
LRVO_ENCRYPTION_KEYdès qu'un module utilise ce coreClé maîtresse, 64 caractères hexadécimaux (32 octets).

npx @lrvo/start remplit cette variable à la création du projet. Sur un projet monté à la main, générer une clé :

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

API

isEncryptionAvailable()

Retourne true si la clé est définie et bien formée, sans jamais lever. Sert aux chemins qui doivent se désactiver plutôt que crasher quand la clé manque, typiquement la vérification d'activation d'un module.

if (!isEncryptionAvailable()) {
  return { enabled: false, reason: 'LRVO_ENCRYPTION_KEY absente' };
}

encryptSecret(plain)

Chiffre une chaîne UTF-8 et retourne la valeur encodée. Lève si la clé est absente ou mal formée, ou si l'argument n'est pas une chaîne.

const stored = encryptSecret(apiToken);
await updateById('lrvo_blog_connectors', id, { token: stored });

decryptSecret(encoded)

Déchiffre une valeur produite par encryptSecret. Lève si la clé est absente, si le format est invalide, ou si le tag d'authentification ne correspond pas : ce dernier cas signale que la valeur a été altérée ou chiffrée avec une autre clé.

const row = await findById('lrvo_blog_connectors', id);
const apiToken = decryptSecret(row.token);

Format stocké

"<iv_b64>:<tag_b64>:<ciphertext_b64>"

Trois champs séparés par :, chacun en base64 standard. L'algorithme est AES-256-GCM, avec un IV de 12 octets tiré aléatoirement à chaque chiffrement et un tag d'authentification de 16 octets. Deux chiffrements de la même valeur produisent donc deux chaînes différentes, ce qui empêche de reconnaître deux secrets identiques en comparant les colonnes.

La colonne qui reçoit cette valeur doit accepter du texte de longueur variable (TEXT), pas une taille fixe.

Notes

La rotation de clé impose de tout re-chiffrer. Il n'y a pas de mécanisme de versionnage de clé : changer LRVO_ENCRYPTION_KEY rend illisible tout ce qui a été chiffré avec l'ancienne. Traiter la clé comme permanente pour un déploiement donné, sauf à planifier une migration qui déchiffre avec l'ancienne clé et re-chiffre avec la nouvelle.

La clé n'a pas sa place dans le dépôt. Elle vit dans le .env du serveur, comme tout secret. Voir Sécurité.

La clé est mise en cache en mémoire tant que la valeur de la variable d'environnement ne change pas, donc l'appel répété de encryptSecret ne re-parse pas la clé à chaque fois.

Sur cette page