Protéger les données financières
Scénario réel : une fintech stocke des IBANs, des tokens de carte et des montants de transaction. Ce guide montre comment chiffrer chaque type de données avec une clé dédiée, afficher des données masquées et exporter les journaux d'audit pour l'ACPR (conformité DORA).
Conformité DORA & DSP2
DORA (règlement UE 2022/2554) impose aux entités financières des mesures de protection des données et une traçabilité complète. Ce pattern utilise des clés séparées par type de données pour satisfaire les exigences d'isolation et d'audit.
1. Stratégie de clés par type de données
key-ibanIBANs & BICs
Coordonnées bancaires
key-cardsTokens carte
Tokens PSP (jamais le PAN complet)
key-transactionsMontants
Montants & références
2. Schéma de la base de données
-- Schéma PostgreSQL : données financières chiffrées
CREATE TABLE bank_accounts (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
iban JSONB NOT NULL, -- chiffré avec key-iban
iban_last4 TEXT NOT NULL, -- 4 derniers caractères (affichage masqué)
bic JSONB NOT NULL, -- chiffré avec key-iban
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE payment_cards (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
card_token JSONB NOT NULL, -- token PSP chiffré avec key-cards
last4 TEXT NOT NULL, -- 4 derniers chiffres (affichage)
exp_month INTEGER NOT NULL,
exp_year INTEGER NOT NULL,
brand TEXT NOT NULL, -- Visa, Mastercard…
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE transactions (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
amount_enc JSONB NOT NULL, -- chiffré avec key-transactions
currency TEXT NOT NULL,
direction TEXT NOT NULL, -- credit | debit
reference TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);3. Chiffrement des IBANs
L'IBAN est chiffré avant insertion. Seuls les 4 derniers caractères sont stockés en clair pour l'affichage masqué (ex: FR•• •••• ••89). Le déchiffrement complet n'est réalisé que pour les virements.
import { Mafate } from '@mafate/sdk'
const mafate = new Mafate({ apiKey: process.env.MAFATE_API_KEY })
// Enregistrer un IBAN chiffré
async function storeIban(userId: number, iban: string, bic: string) {
// Normaliser : supprimer espaces, mettre en majuscules
const normalizedIban = iban.replace(/\s/g, '').toUpperCase()
// Chiffrer avec la clé dédiée aux données bancaires
const [encIban, encBic] = await Promise.all([
mafate.encryptLocal(normalizedIban, 'key-iban'),
mafate.encryptLocal(bic, 'key-iban'),
])
// Garder les 4 derniers caractères pour affichage masqué
const ibanLast4 = normalizedIban.slice(-4)
await db.query(
`INSERT INTO bank_accounts (user_id, iban, iban_last4, bic)
VALUES ($1, $2, $3, $4)`,
[userId, JSON.stringify(encIban), ibanLast4, JSON.stringify(encBic)]
)
}
// Affichage masqué sans déchiffrement
async function getMaskedAccounts(userId: number) {
const rows = await db.query(
'SELECT id, iban_last4, created_at FROM bank_accounts WHERE user_id = $1',
[userId]
)
// → "FR•• •••• •••• •••• •••• •••X XXXX" (seulement les 4 derniers)
return rows.rows.map(r => ({
id: r.id,
maskedIban: `FR•• •••• •••• •••• ••${r.iban_last4}`,
createdAt: r.created_at,
}))
}
// Déchiffrer l'IBAN complet (ex: pour un virement)
async function getFullIban(accountId: number): Promise<string> {
const row = await db.query(
'SELECT iban FROM bank_accounts WHERE id = $1',
[accountId]
)
return await mafate.decryptLocalToString(JSON.parse(row.rows[0].iban))
}4. Export audit pour l'ACPR
MAFATE journalise chaque opération de chiffrement et déchiffrement. Exportez les journaux pour répondre aux exigences DORA de traçabilité sur 5 ans.
-- Export d'audit pour l'ACPR (Autorité de contrôle prudentiel)
-- Toutes les opérations sur les données financières sur 90 jours
SELECT
al.id,
al.operation, -- encrypt | decrypt | rotate
al.key_id,
al.performed_at,
al.api_key_id,
al.source_ip,
al.metadata->>'userId' AS user_id
FROM audit_logs al
WHERE al.key_id IN ('key-iban', 'key-cards', 'key-transactions')
AND al.performed_at >= NOW() - INTERVAL '90 days'
ORDER BY al.performed_at DESC;5. Rotation des clés financières
Recommandation : rotation automatique tous les 180 jours pour key-iban et key-cards. Après rotation, les anciens ciphertexts restent déchiffrables grâce au key_version stocké, aucune migration de données requise.