Verification de la chaine
Une chaine de hash n'est utile que si vous la verifiez regulierement. AuditChain est livre avec une commande Artisan et une API programmatique qui recalculent chaque hash de la chaine et signalent toute incoherence — detectant ainsi la falsification, la corruption de donnees ou les modifications accidentelles.
La commande audit:verify
# Verify the entire chain php artisan audit:verify # Filter by model type php artisan audit:verify --type="App\Models\User" # Filter by model type and ID php artisan audit:verify --type="App\Models\User" --id=42 # Verify and send notifications on failure php artisan audit:verify --notify
Flags
| Flag | Description |
|---|---|
--type= |
Limite la verification a une seule classe de model Eloquent (par ex. App\Models\Invoice). |
--id= |
Combine avec --type, limite a une seule instance de model. |
--notify |
Envoie des notifications par mail et/ou webhook si la chaine est compromise. |
Verification filtree vs. verification complete
AuditChain utilise une chaine globale — une seule chaine de hash qui couvre tous les models auditables. Lorsque vous executez audit:verify sans filtres, deux verifications sont effectuees sur chaque entree :
- Integrite du hash — recalcul du hash a partir des donnees stockees et comparaison avec le
hashenregistre. - Continuite de la chaine — confirmation que le
prev_hashde l'entree correspond auhashde l'entree precedente.
Lorsque vous utilisez --type ou --id, seule l'integrite du hash est verifiee. La continuite de la chaine est ignoree car le sous-ensemble filtre ne contient pas chaque maillon de la chaine globale.
Fonctionnement de la verification
La verification traite les journaux d'audit par lots de 1 000, tries par id croissant (les ULIDs sont monotones, donc le tri par id suffit). La vérification s'arrête prématurément après maxErrors échecs (par défaut : 100) pour éviter de traiter une chaîne entièrement corrompue. Pour chaque entree, le service :
- Lit les valeurs brutes de la base de donnees (en contournant les casts Eloquent pour eviter les ecarts de format).
- Normalise l'horodatage
created_aten UTC. - Trie les tableaux
old_values,new_valuesetpersonal_data_accessedpour un ordre de cles deterministe. - Construit le meme payload canonique que celui utilise lors de l'enregistrement.
- Calcule
SHA-256sur le payload encode en JSON. - Compare le resultat avec le
hashstocke.
S'il s'agit d'une execution non filtree, le service verifie egalement que le prev_hash de l'entree correspond au hash de l'entree precedente (ou au hash de genese pour la premiere entree).
Verification basee sur les checkpoints
Si des logs d'audit chaînés ont été purgés avec --include-chained, la colonne audit_chain_state.checkpoint_hash stocke le hash de la dernière entrée supprimée. La vérification utilise ce checkpoint comme point de départ au lieu du hash de genèse, de sorte que la chaîne restante puisse toujours être vérifiée avec succès.
API programmatique
Utilisez AuditChainService directement lorsque vous devez verifier la chaine dans le code applicatif — par exemple dans un endpoint de health check ou une commande personnalisee :
use GrayMatter\AuditChain\Services\AuditChainService; $service = app(AuditChainService::class); // Full chain $result = $service->verifyChain(); // Scoped to a model type $result = $service->verifyChain(type: 'App\Models\Invoice'); // Scoped to a single record $result = $service->verifyChain(type: 'App\Models\Invoice', id: '42'); // Limiter l'arrêt prématuré (par défaut : 100 erreurs) $result = $service->verifyChain(maxErrors: 50);
La valeur de retour est un tableau :
[
'valid' => true, // bool -- true if no errors found
'checked' => 150, // int -- number of entries verified
'errors' => [], // array of ['id' => '...', 'message' => '...']
]
Exemple : endpoint de health check
Route::get('/health/audit-chain', function () { $result = app(AuditChainService::class)->verifyChain(); return response()->json($result, $result['valid'] ? 200 : 500); });
Planification de la verification
Pour les systemes en production, planifiez audit:verify --notify afin d'etre alerte automatiquement lorsqu'une falsification est detectee. Avec Laravel 11+, ajoutez la planification dans routes/console.php :
use Illuminate\Support\Facades\Schedule; // Verify the chain every hour and notify on failure Schedule::command('audit:verify --notify')->hourly();
Ajustez la frequence en fonction du delai que vous pouvez tolerer entre un evenement de falsification et sa detection. Pour les environnements a haute securite, envisagez une execution toutes les 15 minutes :
Schedule::command('audit:verify --notify')->everyFifteenMinutes();
Notifications
Lorsque --notify est passe et que la verification echoue, AuditChain envoie des alertes via les canaux configures dans config/audit-chain.php :
'notifications' => [ 'channels' => ['mail', 'webhook'], 'mail_to' => [env('AUDIT_ALERT_EMAIL', '')], 'webhooks' => [ env('AUDIT_ALERT_WEBHOOK_1'), ], ],
- Mail — envoie une notification
ChainCompromisedaux adresses configurees. - Webhook — envoie un payload JSON en POST a chaque URL. Le payload inclut les cles
textetcontentpour la compatibilite avec Slack, Microsoft Teams, Discord et les endpoints personnalises.
Le payload webhook inclut le nombre d'erreurs, le nombre total d'entrees verifiees et jusqu'a 5 details d'erreurs individuelles.
Que faire en cas d'echec de la verification
Un echec de verification signifie qu'une ou plusieurs entrees du journal d'audit ont ete modifiees ou que la chaine a ete rompue. Traitez cela comme un incident de securite :
- Investiguez immediatement — la sortie d'erreur inclut l'ULID de chaque entree concernee.
- Verifiez les journaux d'acces a la base de donnees — recherchez des requetes
UPDATEouDELETEdirectes sur la table d'audit. - Examinez les journaux applicatifs — verifiez les deployments, migrations ou operations de seed inattendus.
- Preservez les preuves — prenez un snapshot de la base de donnees avant toute modification.
- Notifiez les parties prenantes — selon vos exigences de conformite (RGPD Art. 33, NIS2 Art. 21), vous devrez peut-etre signaler l'incident.