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 :

  1. Integrite du hash — recalcul du hash a partir des donnees stockees et comparaison avec le hash enregistre.
  2. Continuite de la chaine — confirmation que le prev_hash de l'entree correspond au hash de 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 :

  1. Lit les valeurs brutes de la base de donnees (en contournant les casts Eloquent pour eviter les ecarts de format).
  2. Normalise l'horodatage created_at en UTC.
  3. Trie les tableaux old_values, new_values et personal_data_accessed pour un ordre de cles deterministe.
  4. Construit le meme payload canonique que celui utilise lors de l'enregistrement.
  5. Calcule SHA-256 sur le payload encode en JSON.
  6. Compare le resultat avec le hash stocke.

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 ChainCompromised aux adresses configurees.
  • Webhook — envoie un payload JSON en POST a chaque URL. Le payload inclut les cles text et content pour 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 :

  1. Investiguez immediatement — la sortie d'erreur inclut l'ULID de chaque entree concernee.
  2. Verifiez les journaux d'acces a la base de donnees — recherchez des requetes UPDATE ou DELETE directes sur la table d'audit.
  3. Examinez les journaux applicatifs — verifiez les deployments, migrations ou operations de seed inattendus.
  4. Preservez les preuves — prenez un snapshot de la base de donnees avant toute modification.
  5. Notifiez les parties prenantes — selon vos exigences de conformite (RGPD Art. 33, NIS2 Art. 21), vous devrez peut-etre signaler l'incident.