Permissions de base de donnees
Les gardes Eloquent boot d'AuditChain empechent les mises a jour et suppressions au niveau applicatif, mais une application compromise peut contourner Eloquent et executer du SQL brut. Un utilisateur base de donnees dedie avec des privileges restreints comble cette lacune en imposant l'immutabilite au niveau de la base de donnees.
Pourquoi c'est important
Le model AuditLog lance une RuntimeException si vous tentez de mettre a jour ou de supprimer un enregistrement via Eloquent. C'est efficace contre les modifications accidentelles et les bugs applicatifs, mais cela ne protege pas contre :
- Les requetes SQL directes (
DB::statement('DELETE FROM audit_logs ...')) - Une application compromise executant des commandes base de donnees arbitraires
- Un attaquant ayant acces aux identifiants base de donnees utilises par l'application principale
- Les outils de gestion de base de donnees (phpMyAdmin, TablePlus, etc.) connectes avec l'utilisateur applicatif
En donnant a la connexion d'audit un utilisateur base de donnees qui ne peut qu'inserer et lire (INSERT et SELECT), vous garantissez que les journaux d'audit ne peuvent etre ni modifies ni supprimes, meme si l'application est entierement compromise.
Mise en place d'une connexion dediee
1. Creer un utilisateur base de donnees restreint
MySQL
-- Create the user CREATE USER 'audit_writer'@'%' IDENTIFIED BY 'a-strong-random-password'; -- Grant only INSERT and SELECT on the audit table GRANT INSERT, SELECT ON your_database.audit_logs TO 'audit_writer'@'%'; -- Apply changes FLUSH PRIVILEGES;
PostgreSQL
-- Create the user CREATE USER audit_writer WITH PASSWORD 'a-strong-random-password'; -- Grant only INSERT and SELECT on the audit table GRANT INSERT, SELECT ON audit_logs TO audit_writer; -- Grant USAGE on the schema (required for PostgreSQL) GRANT USAGE ON SCHEMA public TO audit_writer;
Si votre table d'audit utilise une sequence pour une colonne (peu courant avec les ULID, mais possible), vous devrez egalement executer
GRANT USAGE, SELECT ON SEQUENCE audit_logs_id_seq TO audit_writer;dans PostgreSQL.
2. Ajouter la connexion dans config/database.php
Definissez une nouvelle connexion utilisant l'utilisateur restreint :
// config/database.php 'connections' => [ // Your main application connection 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'database' => env('DB_DATABASE', 'your_database'), 'username' => env('DB_USERNAME', 'app_user'), 'password' => env('DB_PASSWORD', ''), // ... ], // Dedicated audit connection (INSERT + SELECT only) 'audit' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'database' => env('DB_DATABASE', 'your_database'), 'username' => env('AUDIT_DB_USERNAME', 'audit_writer'), 'password' => env('AUDIT_DB_PASSWORD', ''), // ...remaining options same as your main connection ], ],
3. Pointer AuditChain vers la connexion restreinte
Definissez la connexion dans config/audit-chain.php :
'connection' => 'audit',
Ou via votre fichier .env si vous preferez :
AUDIT_DB_USERNAME=audit_writer AUDIT_DB_PASSWORD=a-strong-random-password
Execution des migrations
L'utilisateur restreint audit_writer ne peut pas creer ou modifier des tables. Executez les migrations en utilisant votre connexion applicative principale (celle par defaut). La migration d'AuditChain ne specifie pas de connexion, elle utilise donc automatiquement la connexion base de donnees par defaut.
Purge des journaux d'audit
La commande audit:prune supprime les anciens journaux d'audit. Comme l'utilisateur restreint ne dispose pas du privilege DELETE, la purge echouera si la connexion d'audit est la seule disponible. Deux approches :
Option A : Executez la commande de purge avec une connexion differente disposant des privileges DELETE. Vous pouvez surcharger temporairement la connexion dans votre tache planifiee :
Schedule::command('audit:prune --days=365') ->daily() ->before(function () { config(['audit-chain.connection' => 'mysql']); }) ->after(function () { config(['audit-chain.connection' => 'audit']); });
Option B : Executez la requete de purge manuellement en utilisant votre connexion base de donnees principale en dehors d'AuditChain.
Verification des permissions
Confirmez que l'utilisateur restreint ne peut pas modifier ni supprimer les enregistrements d'audit :
-- These should succeed INSERT INTO audit_logs (id, auditable_type, auditable_id, event, created_at) VALUES ('test', 'App\\Models\\User', '1', 'test', NOW()); SELECT * FROM audit_logs WHERE id = 'test'; -- These should fail with a permission error UPDATE audit_logs SET event = 'tampered' WHERE id = 'test'; DELETE FROM audit_logs WHERE id = 'test';
Nettoyez la ligne de test en utilisant votre utilisateur applicatif principal apres la verification.
Defense en profondeur
Les permissions au niveau base de donnees constituent une couche de la strategie d'immutabilite d'AuditChain :
| Couche | Protege contre |
|---|---|
| Gardes Eloquent boot | Mises a jour/suppressions au niveau applicatif via Eloquent |
$fillable strict |
Assignation de masse d'attributs non autorises |
| Permissions utilisateur BD | SQL brut, application compromise, acces direct a la BD |
| Verification de la chaine de hash | Toute modification, quelle que soit la methode utilisee |
Aucune couche n'est suffisante a elle seule. Ensemble, elles fournissent une assurance solide que votre journal d'audit n'a pas ete falsifie.