Conformité et audit
Conformité réglementaire et audit dans les systèmes distribués : journaux d'audit, RGPD, chiffrement des données, rétention et traçabilité des actions.
Pourquoi la conformité compte en architecture
La conformité réglementaire n’est pas seulement une question juridique — elle a des implications directes sur la conception et le fonctionnement d’un système. Des réglementations comme le RGPD, PCI-DSS ou SOC 2 imposent des exigences techniques concrètes sur la manière dont les données sont stockées, traitées et protégées.
Ignorer la conformité lors de la conception de l’architecture signifie devoir effectuer des changements coûteux par la suite, ou pire, faire face à des sanctions légales et à une perte de confiance des utilisateurs.
Journaux d’audit
Les journaux d’audit enregistrent qui a fait quoi, quand et sur quelle ressource. Ils constituent la base de toute stratégie de conformité.
Que faut-il enregistrer
Tout ne nécessite pas un journal d’audit. Concentrez-vous sur :
- Authentification : connexion réussie, connexion échouée, déconnexion, changement de mot de passe
- Autorisation : accès refusé, changement de rôles ou de permissions
- Données sensibles : lecture, création, modification ou suppression de données personnelles
- Configuration : changements de configuration du système, déploiements
- Opérations critiques : transactions financières, suppression de comptes, export de données
Structure d’un journal d’audit
{
"timestamp": "2024-01-15T14:30:00.000Z",
"eventType": "DATA_ACCESS",
"action": "READ",
"actor": {
"userId": "user-456",
"role": "admin",
"ip": "192.168.1.100",
"userAgent": "Mozilla/5.0..."
},
"resource": {
"type": "user_profile",
"id": "user-789",
"fields": ["email", "phone", "address"]
},
"result": "SUCCESS",
"metadata": {
"service": "users-service",
"requestId": "req-abc-123",
"traceId": "trace-xyz-789"
}
}
Propriétés d’un bon journal d’audit
| Propriété | Description |
|---|---|
| Immuable | Une fois écrit, il ne peut être ni modifié ni supprimé |
| Complet | Contient toutes les informations nécessaires pour reconstituer l’événement |
| Traçable | Peut être corrélé avec d’autres journaux via un requestId ou un traceId |
| Sécurisé | Protégé contre les accès non autorisés et les manipulations |
| Conservé | Stocké pendant la durée requise par la réglementation |
Implémentation pratique
class AuditLogger {
async log(event) {
const auditEntry = {
timestamp: new Date().toISOString(),
eventType: event.type,
action: event.action,
actor: {
userId: event.userId,
role: event.userRole,
ip: event.clientIp,
},
resource: {
type: event.resourceType,
id: event.resourceId,
},
result: event.result,
metadata: {
service: process.env.SERVICE_NAME,
requestId: event.requestId,
traceId: event.traceId,
}
};
// Écrire dans un stockage immuable
await this.auditStore.append(auditEntry);
// Ne jamais échouer silencieusement en audit
// Si l'enregistrement échoue, l'opération doit échouer
}
}
Stockage des journaux d’audit
Les journaux d’audit nécessitent un stockage particulier :
- Append-only : on ne peut qu’ajouter des enregistrements, jamais les modifier ni les supprimer
- Séparé : dans une base de données ou un service différent de celui de l’application
- Chiffré : les journaux peuvent contenir des données sensibles
- Répliqué : copies dans plusieurs emplacements pour éviter toute perte
Options courantes :
- Amazon S3 avec Object Lock (immutabilité)
- Elasticsearch avec des index en lecture seule
- Base de données dédiée avec permissions d’insertion uniquement
- Services spécialisés comme Splunk ou Datadog
RGPD et protection des données personnelles
Le Règlement Général sur la Protection des Données (RGPD) de l’Union Européenne établit des exigences strictes sur la manière dont les données personnelles sont traitées. Bien qu’il s’agisse d’une réglementation européenne, elle concerne tout système traitant des données de résidents de l’UE.
Principes clés du RGPD
- Minimisation des données : ne collecter que les données strictement nécessaires
- Limitation de la finalité : utiliser les données uniquement pour l’objectif déclaré
- Limitation de la conservation : ne pas conserver les données plus longtemps que nécessaire
- Intégrité et confidentialité : protéger les données contre tout accès non autorisé
- Responsabilité proactive : démontrer la conformité, pas seulement la respecter
Droits de l’utilisateur ayant un impact sur l’architecture
Droit d’accès (Art. 15)
L’utilisateur peut demander toutes les données que le système possède à son sujet :
// Le système doit pouvoir collecter les données de TOUS les services
async function getUserData(userId) {
const [profile, orders, payments, logs] = await Promise.all([
usersService.getData(userId),
ordersService.getData(userId),
paymentsService.getData(userId),
auditService.getData(userId),
]);
return {
personalData: profile,
orderHistory: orders,
paymentHistory: payments,
activityLog: logs,
exportedAt: new Date().toISOString()
};
}
Cela implique que chaque microservice doit pouvoir exporter les données d’un utilisateur spécifique.
Droit à l’oubli (Art. 17)
L’utilisateur peut demander la suppression de toutes ses données :
async function deleteUserData(userId) {
// Supprimer de tous les services
await usersService.deleteUser(userId);
await ordersService.anonymizeOrders(userId);
await paymentsService.anonymizePayments(userId);
// Certaines données ne peuvent pas être supprimées (obligations légales)
// mais peuvent être anonymisées
await auditService.anonymizeEntries(userId);
// Enregistrer la suppression (paradoxalement, celle-ci est bien conservée)
await auditLogger.log({
type: 'DATA_DELETION',
action: 'DELETE',
userId: 'ANONYMIZED',
resourceId: userId
});
}
Droit à la portabilité (Art. 20)
L’utilisateur peut demander ses données dans un format lisible par machine :
{
"format": "JSON",
"exportDate": "2024-01-15",
"userData": {
"profile": { "name": "María García", "email": "maria@ejemplo.com" },
"orders": [
{ "id": "ord-001", "date": "2024-01-10", "total": 150.00 }
]
}
}
Implications architecturales du RGPD
- Chaque service doit savoir quelles données personnelles il stocke
- Un mécanisme centralisé doit exister pour exécuter les droits de l’utilisateur
- Les données doivent pouvoir être anonymisées sans compromettre l’intégrité du système
- Les journaux d’audit doivent équilibrer rétention et droit à l’oubli
Chiffrement des données
Chiffrement en transit
Toute donnée circulant sur le réseau doit être chiffrée :
- TLS 1.2+ pour la communication externe
- mTLS pour la communication entre services
- Chiffrement des messages dans les files d’attente et les bus d’événements
Chiffrement au repos
Les données stockées doivent être chiffrées :
Niveaux de chiffrement au repos :
1. Chiffrement de disque (transparent)
- AWS EBS encryption, Azure Disk Encryption
- Protège contre le vol physique du disque
2. Chiffrement de base de données (TDE)
- Transparent Data Encryption dans PostgreSQL, MySQL
- Protège contre l'accès direct aux fichiers de la BD
3. Chiffrement au niveau des champs (application-level)
- Champs spécifiques chiffrés par l'application
- Protège même si la BD est compromise
Chiffrement au niveau des champs
Pour les données particulièrement sensibles, chiffrez les champs individuellement :
const crypto = require('crypto');
class FieldEncryption {
constructor(key) {
this.key = Buffer.from(key, 'hex');
}
encrypt(plaintext) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
const tag = cipher.getAuthTag().toString('hex');
return `${iv.toString('hex')}:${tag}:${encrypted}`;
}
decrypt(ciphertext) {
const [ivHex, tagHex, encrypted] = ciphertext.split(':');
const iv = Buffer.from(ivHex, 'hex');
const tag = Buffer.from(tagHex, 'hex');
const decipher = crypto.createDecipheriv('aes-256-gcm', this.key, iv);
decipher.setAuthTag(tag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// Utilisation
const encryption = new FieldEncryption(process.env.ENCRYPTION_KEY);
const encryptedSSN = encryption.encrypt('123-45-6789');
Gestion des clés
Les clés de chiffrement sont aussi sensibles que les données qu’elles protègent :
- Ne jamais stocker les clés dans le code source
- Utiliser un service de gestion des clés (KMS) : AWS KMS, Azure Key Vault, HashiCorp Vault
- Faire tourner les clés périodiquement
- Conserver des clés distinctes par environnement (développement, staging, production)
- Implémenter l’envelope encryption pour les données à grande échelle
Rétention des données
Chaque type de donnée a une période de rétention différente selon la réglementation et la finalité :
| Type de donnée | Rétention typique | Raison |
|---|---|---|
| Journaux d’audit | 1 à 7 ans | Conformité réglementaire |
| Données de transactions | 5 à 10 ans | Obligations fiscales |
| Données personnelles | Tant que nécessaire | RGPD : minimisation |
| Journaux d’application | 30 à 90 jours | Débogage et opérations |
| Sauvegardes | 30 à 365 jours | Reprise après sinistre |
Implémentation des politiques de rétention
# Politique de rétention par type de donnée
retention_policies:
audit_logs:
retention_days: 2555 # 7 ans
storage: s3-archive
encryption: aes-256
immutable: true
application_logs:
retention_days: 90
storage: elasticsearch
auto_delete: true
user_data:
retention: until_deletion_request
anonymization: on_request
backup_retention_days: 30
transaction_data:
retention_days: 3650 # 10 ans
storage: database
anonymization: after_retention
Traçabilité des actions
Dans un système distribué, une action de l’utilisateur peut impliquer plusieurs services. La traçabilité permet de reconstituer le chemin complet :
L'utilisateur demande la suppression de son compte
→ API Gateway (requête reçue)
→ BFF (orchestre la suppression)
→ Users Service (supprime le profil)
→ Orders Service (anonymise les commandes)
→ Payments Service (anonymise les paiements)
→ Notifications Service (envoie la confirmation)
→ Audit Service (enregistre l'opération complète)
Correlation ID
Chaque requête génère un identifiant unique qui se propage à tous les services :
// Middleware qui génère ou propage le correlation ID
function correlationMiddleware(req, res, next) {
const correlationId = req.headers['x-correlation-id'] || uuid();
req.correlationId = correlationId;
res.setHeader('x-correlation-id', correlationId);
// Inclure dans tous les journaux
req.logger = logger.child({ correlationId });
next();
}
Grâce au correlation ID, vous pouvez chercher dans les journaux de tous les services et reconstituer exactement ce qui s’est passé à chaque étape.
Checklist de conformité
Pour vérifier que votre architecture répond aux exigences de base :
- Les journaux d’audit sont immuables et chiffrés
- Chaque service peut exporter les données d’un utilisateur spécifique
- Il existe un mécanisme pour supprimer ou anonymiser les données d’un utilisateur
- Les données en transit sont chiffrées (TLS/mTLS)
- Les données au repos sont chiffrées
- Les clés de chiffrement sont gérées dans un KMS, pas dans le code
- Des politiques de rétention sont définies par type de donnée
- Chaque requête possède un correlation ID pour la traçabilité
- Les accès aux données sensibles sont enregistrés dans le journal d’audit
- Les données personnelles sont collectées avec un consentement explicite
Résumé
La conformité réglementaire et l’audit sont des responsabilités architecturales, pas seulement juridiques. Les journaux d’audit immuables constituent la base pour démontrer la conformité. Le RGPD impose des exigences concrètes sur la manière dont les données personnelles sont stockées, exportées et supprimées — et ces exigences influencent directement la conception de chaque microservice. Le chiffrement protège les données en transit et au repos, et la gestion des clés est aussi critique que le chiffrement lui-même. La traçabilité via des correlation IDs permet de reconstituer n’importe quelle action à travers plusieurs services. Concevoir en pensant à la conformité dès le départ est bien plus économique que de s’adapter par la suite.