Guide pratique
Audit et conformité
Comment interroger les journaux d'audit MAFATE, exporter les données pour les régulateurs (CNIL, ACPR, ANSSI) et vérifier l'intégrité de la chaîne HMAC.
RGPD
Art. 30 & 32
Registre des traitements et traçabilité des accès
NIS2
Art. 21
Journaux d'audit pour la gestion des incidents
DORA
Art. 9 & 10
Traçabilité sur 5 ans pour les entités financières
1. Structure d'une entrée d'audit
// Exemple d'entrée de journal d'audit MAFATE
{
"id": "al_01HXK9B2YZRPQ3VWMN4T5J6D7",
"operation": "decrypt", // encrypt | decrypt | rotate | key.create | key.disable
"key_id": "production-users-pii",
"key_version": 2, // version de clé utilisée
"api_key_id": "ak_01HXK9B2YZ...", // quelle clé API a réalisé l'opération
"sourceIp": "203.0.113.42", // IP source de la requête
"userAgent": "mafate-sdk-node/2.1.0",
"metadata": { // contexte optionnel passé par l'application
"userId": "usr_12345",
"requestId": "req_abc123"
},
"prevHash": "sha256:7f3a2b...", // hash HMAC de l'entrée précédente (immutabilité)
"performedAt": "2026-03-18T14:22:05.123Z"
}2. Interroger les journaux
import { Mafate } from '@mafate/sdk'
const mafate = new Mafate({ apiKey: process.env.MAFATE_API_KEY })
// Les 100 dernieres operations. ⚠️ 100 est le PLAFOND serveur : demander
// davantage n'echoue pas, cela renvoie 100. Comparez toujours count.
const page = await mafate.audit.list({ limit: 100 })
// page.logs -> AuditEntry[] ; page.count ; page.total ; page.limit ; page.offset
// Filtrer. `action` est unique, il n'existe pas de filtre `operations`.
const desemballages = await mafate.audit.list({
key_id: KEY_ID,
date_from: '2026-01-01T00:00:00Z',
date_to: '2026-03-31T23:59:59Z',
action: 'key.unwrap',
limit: 100,
})
// Parcourir TOUT le journal : par offset, il n'existe pas de curseur.
let offset = 0
let total = Infinity
while (offset < total) {
const p = await mafate.audit.list({ limit: 100, offset })
total = p.total
for (const entree of p.logs) {
console.log(entree.created_at, entree.action, entree.key_id, entree.actor)
}
if (p.count === 0) break // garde-fou : jamais de boucle infinie sur un journal vide
offset += p.count
}3. Export pour les régulateurs
Exportez les journaux au format CSV pour les soumettre à la CNIL, l'ACPR ou l'ANSSI lors d'un audit. MAFATE conserve les journaux pendant 5 ans.
// Export CSV pour un dossier RGPD, NIS2 ou DORA.
//
// ⛔ LE PLAFOND DE 100 EST LE PIEGE DE CET EXPORT. La version precedente
// demandait limit: 500 et considerait la page comme complete : l'export
// s'arretait a 100 lignes, sans erreur, et personne ne le voyait avant
// le controle.
async function exportAuditCsv(dateFrom: string, dateTo: string): Promise<string> {
const rows: string[] = ['id,action,key_id,key_version,actor,ip_address,created_at']
let offset = 0
let total = Infinity
while (offset < total) {
const page = await mafate.audit.list({
date_from: dateFrom,
date_to: dateTo,
limit: 100,
offset,
})
total = page.total
for (const e of page.logs) {
rows.push(
[e.id, e.action, e.key_id ?? '', e.key_version ?? '', e.actor, e.ip_address ?? '', e.created_at].join(',')
)
}
if (page.count === 0) break
offset += page.count
}
// Controle de completude : sans lui, un export tronque se lit comme un
// export complet.
if (rows.length - 1 !== total) {
throw new Error(`export incomplet : ${rows.length - 1} lignes pour ${total} attendues`)
}
return rows.join('\n')
}
const csv = await exportAuditCsv('2026-01-01T00:00:00Z', '2026-03-31T23:59:59Z')
await fs.writeFile('audit-t1-2026.csv', csv, 'utf8')4. Vérification de l'intégrité
Chaque entrée d'audit inclut le hash HMAC de l'entrée précédente, formant une chaîne. Vérifiez cette chaîne pour prouver qu'aucune entrée n'a été modifiée ou supprimée depuis sa création.
// Verification de l'integrite du journal.
//
// ⛔ NE RECALCULEZ PAS LA CHAINE VOUS-MEME. La version precedente de ce guide
// rejouait un `mafate.hash` sur chaque entree : ce n'est pas ainsi que le
// chainage est construit, et le controle ne prouvait rien.
//
// ⏩ Le serveur recalcule le HMAC de CHAQUE entree et verifie le chainage.
const verif = await mafate.audit.verifyChain()
// {
// chain_valid: true,
// entries_checked: 14873,
// oldest_entry: '2026-01-02T08:14:22Z',
// newest_entry: '2026-08-19T09:41:07Z',
// first_invalid_entry: null,
// verified_at: '2026-08-19T09:42:11Z'
// }
if (!verif.chain_valid) {
// `first_invalid_entry` porte l'identifiant de la PREMIERE entree rompue.
// Une chaine invalide ne dit pas qui a modifie quoi : elle dit a partir
// d'ou le journal cesse de faire preuve.
throw new Error(`chaine rompue a partir de l'entree ${verif.first_invalid_entry}`)
}
// ⏩ CE QU'UN AUDITEUR PEUT EN FAIRE. La sortie porte la periode couverte et
// l'horodatage du controle : c'est elle qui se joint au dossier, pas une
// reimplementation maison.5. Alertes et anomalies
Configurez des alertes dans le Dashboard MAFATE pour détecter :
- Volume anormal de déchiffrements (ex: > 10 000 en 1 heure)
- Accès depuis une IP inconnue ou un pays non autorisé
- Tentatives de déchiffrement avec une clé révoquée
- Rotation manuelle en dehors des fenêtres de maintenance