Sécurité multi-tenant
Titreo E-Pay héberge les credentials de paiement de plusieurs marchands. La sécurité est donc
multi-tenant par conception. Le code de crypto vit dans api/src/infrastructure/crypto/.
1. Credentials PSP chiffrés au repos (AEAD)
Section intitulée « 1. Credentials PSP chiffrés au repos (AEAD) »Les clés API Stripe/Adyen de chaque marchand sont stockées chiffrées en base, jamais en clair.
- Algorithme : AES-256-GCM (chiffrement authentifié).
api/src/infrastructure/crypto/aead.ts. - Clé maîtresse : la KEK (variable d’environnement
PSP_CONFIG_KEK). - Stockage :
FirebirdMerchantPspConfigRepochiffre à l’écriture, déchiffre à la lecture (api/src/infrastructure/db/firebird-merchant-psp-config-repo.ts, migrations003/005/020).
Les champs sensibles de l’outbox (ex. CVV de carte cadeau, tokens) sont également chiffrés via
payload-crypto.ts avant insertion, avec des données associées (AAD) liées à l’identifiant de la ligne.
2. Authentification : API keys hashées + scopes
Section intitulée « 2. Authentification : API keys hashées + scopes »- Format :
tep_live_<aléatoire>. Génération :api/src/infrastructure/crypto/api-key.ts. - Stockage hashé : seul le hash est en base (
api_keys, migration004) — une fuite de la base ne révèle pas les clés. - En-tête :
Authorization: Bearer tep_live_…. Vérification par le use caseauthenticate-api-key.ts, appelé depuis le pluginapi/src/http/plugins/auth.ts. - Scopes : chaque clé porte des permissions (
sessions:read,sessions:write,psp:write,webhooks:write,admin). Le pluginrequire-scope.tsrejette en403si le scope manque.
Au-delà des API keys (intégrations serveur), il existe :
- des tokens de session client (signés, courte durée) pour le navigateur —
session-token.ts; - des sessions utilisateur (cookies) pour les consoles admin/marchand —
user-session-token.ts, pluginuser-session.ts; - du hachage de mots de passe par scrypt —
scrypt-password-hasher.ts.
3. Webhooks signés (HMAC, sortants)
Section intitulée « 3. Webhooks signés (HMAC, sortants) »Quand l’API notifie le marchand (session.completed, session.failed…), elle signe la requête :
x-titreo-signature: t=<timestamp>,v1=<hex(HMAC-SHA256(timestamp.payload))>Le marchand recalcule le HMAC avec son secret partagé et compare (comparaison à temps constant). Les
SDK fournissent un vérificateur prêt à l’emploi (verifyMerchantWebhook côté JS, Webhook\Verifier
côté PHP). Une tolérance sur le timestamp (~5 min) limite le rejeu.
4. Webhooks PSP entrants : la bonne URL au bon marchand
Section intitulée « 4. Webhooks PSP entrants : la bonne URL au bon marchand »Un PSP poste sur POST /v1/webhooks/:providerType/:merchantId. L’API résout le secret de webhook du
marchand visé puis vérifie la signature avant de parser le corps (verifyWebhook de l’adapter
PSP). Pas de signature valide → 400, sans effet de bord.
5. Manifest navigateur signé (Ed25519)
Section intitulée « 5. Manifest navigateur signé (Ed25519) »Le bootstrap navigateur (GET /v1/sessions/:id/bootstrap) renvoie un manifest signé par une clé
privée Ed25519 (asymétrique). api/src/infrastructure/crypto/ed25519-signer.ts. La clé publique
est embarquée dans le loader, qui refuse de charger des adapters dont le manifest n’est pas
correctement signé. Chaque module d’adapter porte aussi une intégrité SRI (sha384-…).
6. Protections réseau & journaux
Section intitulée « 6. Protections réseau & journaux »url-guard.ts: empêche l’envoi de webhooks vers des IP privées en production (anti-SSRF).- Journaux : pino, avec masquage (
redact) des champs sensibles. Jamais de PAN, CVV, carte cadeau en clair ni token PSP dans les logs (voirCONVENTIONS.md). config/index.tsest le seul endroit qui litprocess.env, validé par Zod ; le reste du code reçoit un objetConfigtypé par injection.
Carte mentale
Section intitulée « Carte mentale »flowchart TB
subgraph repos["Au repos (base)"]
A["Configs PSP — AES-256-GCM (KEK)"]
B["API keys — hash"]
C["Mots de passe — scrypt"]
end
subgraph transit["En transit"]
D["Webhooks sortants — HMAC-SHA256"]
E["Webhooks PSP entrants — signature PSP"]
F["Manifest navigateur — Ed25519 + SRI"]
end
Source de vérité complémentaire : section « Sécurité multi-tenant » de
ECOSYSTEM.md, etdoc/site/content/security.mdx.