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 accès cartes cadeaux d’un marchand (nom d’enseigne et clé de l’API Titréo REST) suivent le même
régime : même algorithme, même KEK, table merchant_gift_configs (migration 026). Les données
associées sont gift|<merchantId>|<provider> : une ligne recopiée sous un autre marchand ne se
déchiffre pas. Les accès sont vérifiés auprès de Titréo avant d’être enregistrés, et la clé n’est
jamais renvoyée par l’API (api/src/infrastructure/db/firebird-merchant-gift-config-repo.ts).
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). Le détail de ce qui est censuré, masqué ou remplacé par une empreinte est sur Traçabilité. 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 et accès cartes cadeaux — 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.