
laravel-threat-detection v1.7.2
Middleware Laravel passif qui détecte et journalise les injections SQL, les XSS, les RCE, les scanners de bots et plus de 175 modèles d'attaques. Doté d'un tableau de bord intégré, d'alertes Slack, d'une API REST et d'un enrichissement géographique. IDS, pas WAF.
Détection de menaces Laravel
Surveillance de sécurité et journalisation des attaques pour Laravel. Détectez et journalisez les injections SQL,
les XSS, les RCE, les traversées de répertoire, les scanners de robots et les sondes de reconnaissance de type /wp-admin —
chaque requête hostile est enregistrée dans votre base de données avec le contexte complet de l'application.
C'est un IDS, pas un WAF : il ne bloque, ne filtre ni ne modifie jamais une requête.
Êtes-vous ici parce que vous avez vu quelque chose comme ceci ?```
GET /wp-admin/setup-config.php 404 — on a site that isn't WordPress GET /.env 404 — someone wants your database password GET /?id=1' UNION SELECT password FROM 200 — SQL injection against a real route GET /phpmyadmin/index.php 404 — scanning for an admin panel
Ces requêtes atteignent déjà votre application Laravel. Votre journal d’accès affiche l’URL
et le code de statut, et rien d’autre — ni la charge utile décodée, ni quelle route a été
ciblée, ni si la même IP a tenté quarante autres choses cette heure-ci.
Ce paquet répond à ces questions. Intégrez-le dans n’importe quelle application Laravel 10–13 et il commence
à analyser chaque requête HTTP par rapport à plus de 150 modèles d’attaque, en notant chaque correspondance selon
sa confiance et en l’écrivant dans votre base de données — avec un tableau de bord intégré, des alertes Slack,
un enrichissement géographique et des exports fail2ban/listes de blocage. Aucune requête n’est jamais bloquée. Pensez
caméra de sécurité, pas verrou : il vous montre exactement qui sonde vos routes, à quelle
fréquence, et avec quelles techniques.
> Extrait d’une application en production et éprouvé sur du trafic réel. 335 tests, aucune dépendance
> d’exécution au-delà de Laravel lui-même, et aucune connexion Internet requise pour la détection.
>
> Mise à niveau ? Voir [UPGRADING.md](https://github.com/jay123anta/laravel-threat-detection/blob/main/UPGRADING.md). Contribution ? Voir [CONTRIBUTING.md](https://github.com/jay123anta/laravel-threat-detection/blob/main/CONTRIBUTING.md).
## Commencez en moins d’une minute```bash
composer require jayanta/laravel-threat-detection
php artisan vendor:publish --tag=threat-detection-migrations
php artisan migrate
Ensuite, ajoutez le middleware à votre groupe web (une ligne dans bootstrap/app.php sur Laravel 11+,
ou app/Http/Kernel.php sur Laravel 10) — extrait complet dans Quick Start ci-dessous.
C'est tout ; la détection est active.```bash
php artisan threat-detection:doctor # confirms it is actually recording
---
## Où il se situe : IDS vs WAF vs edge
Ce paquet est un **IDS passif au niveau applicatif** — il observe et enregistre, il ne
bloque pas. Il est conçu pour se placer *aux côtés* d'un WAF ou d'un service edge, pas pour
le remplacer. Chaque couche voit quelque chose que les autres ne voient pas :
| | **Ce paquet** (IDS applicatif) | **WAF** (mod_security, Cloudflare WAF) | **Edge / CDN** (Cloudflare) |
|---|:---:|:---:|:---:|
| Bloque les requêtes malveillantes | ❌ journalise uniquement | ✅ | ✅ |
| Contexte applicatif complet (route exacte, payload décodé, utilisateur authentifié) | ✅ | ⚠️ partiel | ❌ |
| Tableau de bord intégré + journal des menaces dans votre base de données | ✅ | ⚠️ variable | ⚠️ edge uniquement |
| Détections spécifiques à l'application (ex. PII Aadhaar / PAN / IFSC) | ✅ motifs personnalisés | ❌ | ❌ |
| Fonctionne hors ligne / sans service externe | ✅ | ⚠️ selon le cas | ❌ |
| Arrête le trafic avant qu'il n'atteigne votre application | ❌ | ✅ edge | ✅ |
| Installation | un `composer require` | moyen–élevé | faible–moyen |
| Coût | gratuit, MIT | variable | niveau gratuit + payant |
**En bref :** un edge/WAF est votre verrou sur la porte ; ceci est la caméra de sécurité
*à l'intérieur*, avec le contexte applicatif pour vous dire exactement ce qui est tenté sur
quelle route, par qui, et à quelle fréquence. Utilisez-le pour alimenter de vraies
décisions — bans fail2ban, limites de débit, blocage géographique — avec des données que
votre couche edge ne voit jamais.
### Ce qu'il n'est délibérément PAS
- **Pas un WAF.** Il ne bloque, ne filtre et ne modifie jamais une requête. Utilisez
Cloudflare, mod_security ou un vrai WAF pour l'application des règles. (Pas de couche
edge à laquelle déléguer ? Les [aides côté opérateur](#acting-on-the-data-operator-side-blocking)
exposent les décisions du paquet pour que vous puissiez écrire votre propre middleware de
blocage en cinq lignes — le code d'application des règles reste le vôtre, pas celui du paquet.)
- **Pas un remplacement pour un codage sécurisé.** Les requêtes paramétrées, la validation
des entrées et l'échappement des sorties sont vos véritables défenses. Ce paquet suppose
que votre code est déjà sécurisé et vous offre de la *visibilité*, pas de la protection.
- **Pas un service edge.** Si vous pouvez placer Cloudflare devant, faites-le — puis ajoutez
ceci pour le détail au niveau applicatif que les services edge ne peuvent pas voir.
### Alors, qu'en faites-vous concrètement ?
La question la plus courante à propos d'un détecteur qui ne bloque jamais. Quatre réponses,
par ordre croissant d'effort :
| Vous voulez | Utilisez | Effort |
|---|---|---|
| Voir ce qui vous attaque | Le [tableau de bord](#dashboard) ou `threat-detection:stats` | aucun, il tourne déjà |
| Bannir les récidivistes au niveau du pare-feu | [`threat-detection:export-fail2ban`](#artisan-commands) — à diriger vers un cron | une ligne |
| Refuser au niveau du serveur web | [`threat-detection:export-blocklist`](#artisan-commands) → directives nginx/apache | une ligne |
| Refuser les requêtes dans l'application | [Aides côté opérateur](#acting-on-the-data-operator-side-blocking) — `isBlocklisted()`, `isDdosThresholdExceeded()` | ~10 lignes de votre propre middleware |
| Réagir en temps réel | L'[événement `ThreatDetected`](#threatdetected-event) — Telegram, SIEM, PagerDuty | un écouteur |
Le paquet fournit l'intelligence ; vous fournissez le refus. Cette séparation est
délibérée — un code d'application des règles qui vit dans votre application est un code que
vous pouvez lire, tester et désactiver, et cela signifie qu'un bug de détection ne peut
jamais faire tomber votre site.
### Comment il se compare aux autres paquets de sécurité Laravel
Ceux-ci résolvent des problèmes différents et se composent bien — le tableau sert à choisir
le bon outil, pas à gagner.
| Paquet | Ce qu'il fait | Bloque ? | Utilisez-le quand |
|---|---|:---:|---|
| **ce paquet** | Analyse chaque requête contre 150+ motifs, journalise avec le contexte applicatif complet | ❌ | Vous voulez *voir* ce qui est tenté sur votre application |
| `spatie/laravel-honeypot` | Champ de formulaire caché qui attrape les bots de spam | ✅ formulaire uniquement | Vous avez des formulaires publics spammés |
| `graham-campbell/security` | Supprime le balisage de type XSS des entrées | ✅ modifie | Vous voulez un assainissement naïf des entrées |
| `spatie/laravel-csp` | Envoie des en-têtes Content-Security-Policy | ✅ navigateur | Vous voulez contraindre ce que le navigateur charge |
| `laravel/fortify` + limites de débit | Limitation et verrouillage de l'authentification | ✅ | Vous avez besoin d'une protection contre la force brute sur la connexion |
| Cloudflare / mod_security | WAF edge, bloque avant votre application | ✅ | Vous voulez que le trafic soit arrêté avant d'arriver |
Le résumé honnête : un honeypot attrape le spam des formulaires, un WAF bloque le trafic
connu comme malveillant au niveau du edge, et la CSP contraint le navigateur. **Aucun d'eux
ne vous dit ce qu'un attaquant a tenté contre vos routes spécifiques, avec le payload décodé
et l'utilisateur authentifié attaché.** C'est cette lacune que ce paquet comble — et c'est
pourquoi le paquet ne bloque délibérément pas : vous pouvez l'exécuter aux côtés de tout ce
qui précède sans qu'aucun d'eux ne se batte avec les autres.
---
## Prérequis
- PHP 8.2+ (Laravel 13 nécessite PHP 8.3+)
- Laravel 10.x, 11.x, 12.x ou 13.x
- Toute base de données prise en charge par Laravel (MySQL, PostgreSQL, SQLite, SQL Server)
- Tout pilote de cache — **aucun Redis ni worker de file d'attente requis**. Redis/Memcached
n'est *recommandé* que pour activer la vérification DDoS optionnelle (qui se désactive
automatiquement sur les pilotes non atomiques). Les écritures en file d'attente sont
facultatives et désactivées par défaut.
---
## Comment ça fonctionne
1. Un middleware analyse chaque requête HTTP entrante
2. La requête est vérifiée contre 158 motifs regex couvrant l'injection SQL, XSS, RCE, le traversement de fichiers, SSRF, LDAP, XPath, SSTI et plus encore
3. Si un motif de menace correspond, un enregistrement est écrit dans votre table de base de données `threat_logs` avec l'IP, l'URL, le type de menace, le niveau de gravité et un score de confiance
4. En option, une alerte Slack est envoyée pour les menaces de haute gravité
5. La requête se poursuit normalement — **rien n'est bloqué**
Aucune connexion Internet n'est nécessaire pour la détection.
---
## Démarrage rapide
### 1. Installer le paquet```bash
composer require jayanta/laravel-threat-detection
2. Publier les migrations et les exécuter
Cette étape est obligatoire. Sans elle, le package détectera les menaces mais ne pourra pas les stocker dans la base de données. Si vous ignorez cette étape, votre table
threat_logsn'existera pas et toutes les détections seront silencieusement perdues (vous ne verrez que des erreurs dansstorage/logs/laravel.log).```bash php artisan vendor:publish --tag=threat-detection-migrations php artisan migrate
This creates two tables: `threat_logs` (stores detected threats) and `threat_exclusion_rules` (stores false positive rules).
**Verify tables were created:**```bash
php artisan migrate:status
Look for create_threat_logs_table, add_confidence_to_threat_logs_table, et create_threat_exclusion_rules_table - tous devraient afficher Ran.
3. Enregistrer le middleware
Le middleware est ce qui analyse les requêtes. Vous devez l'ajouter à votre groupe de middleware web.
Si vous utilisez Laravel 11 ou 12 - ouvrez bootstrap/app.php :```php
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\JayAnta\ThreatDetection\Http\Middleware\ThreatDetectionMiddleware::class,
]);
})
> **Comment vérifier votre version de Laravel :** Exécutez `php artisan --version` dans votre terminal.
**Si vous utilisez Laravel 10** - ouvrez `app/Http/Kernel.php` :```php
protected $middlewareGroups = [
'web' => [
// ... existing middleware
\JayAnta\ThreatDetection\Http\Middleware\ThreatDetectionMiddleware::class,
],
];
4. (Facultatif) Publier le fichier de configuration```bash
php artisan vendor:publish --tag=threat-detection-config
Le package fonctionne avec des valeurs par défaut raisonnables. La publication de la configuration vous permet de personnaliser les modèles de détection, les modes de sensibilité, les notifications Slack, et plus encore. Si vous ignorez cette étape, tout fonctionne quand même.
**C'est tout.** Votre application détecte désormais les menaces.
---
## Vérifier que cela fonctionne
Après l'installation, déclenchez une menace de test et confirmez qu'elle a été journalisée.
### Étape 1 : Démarrez votre application```bash
php artisan serve
Étape 2 : Ouvrez une URL de test dans votre navigateur
Ajoutez un paramètre de requête malveillant à n’importe quelle route existante de votre application (votre page d’accueil, une page produit, etc.). Par exemple :
Injection SQL :``` http://localhost:8000/?q=' UNION SELECT * FROM users--
**XSS (Cross-Site Scripting) :**```
http://localhost:8000/?q=<script>alert(1)</script>
Traversée de répertoire :``` http://localhost:8000/?file=../../etc/passwd
**RCE (Exécution de code à distance) :**```
http://localhost:8000/?cmd=system('ls -la')
Shellshock (CVE-2014-6271) :``` http://localhost:8000/?cmd=() { :;}; /bin/bash
**Injection de commandes Windows :**```
http://localhost:8000/?cmd=powershell -c whoami
DROP TABLE (DDL SQL) :``` http://localhost:8000/?q=DROP TABLE users
> Utilisez une route qui existe réellement dans votre application (comme `/`). Si l'URL renvoie une 404, le middleware peut ne pas avoir été exécuté.
### Étape 3 : Vérifiez que les menaces ont été journalisées
**Option A - Commande Artisan (la plus rapide) :**```bash
php artisan threat-detection:stats
Option B - Tinker :```bash php artisan tinker
```php
DB::table('threat_logs')->latest()->take(5)->get(['ip_address', 'type', 'threat_level', 'confidence_score']);
Option C - Fichier journal Laravel :
Chaque menace détectée est écrite comme un avertissement dans storage/logs/laravel.log :```
[high] Threat Detected: [middleware] SQL Injection UNION from 127.0.0.1 (http://localhost:8000/?q=...) [confidence: 50%]
### Points à connaître lors des tests
| Comportement | Explication |
|----------|-------------|
| Une même menace n'est journalisée qu'une fois toutes les 5 minutes | Déduplication : la même IP + le même type de menace est mis en cache pendant 5 minutes. Utilisez **différents types d'attaques** pour chaque test, ou attendez entre les tests. |
| Les requêtes `curl` déclenchent une détection supplémentaire | Utiliser `curl` journalise également une détection de user-agent « cURL Command » (gravité faible). C'est normal — le package détecte les outils automatisés. |
| Le package ne bloque jamais les requêtes | Votre application continue de fonctionner normalement. La détection est passive. |
| Aucune configuration Slack requise | Les notifications sont désactivées par défaut. |
| Aucune connexion Internet requise | La détection principale est 100 % locale. Seule la commande optionnelle `threat-detection:enrich` appelle une API externe pour les données géographiques. |
### Dépannage
**Commencez ici — une seule commande répond à la plupart des questions :**```bash
php artisan threat-detection:doctor
Il vérifie les éléments qui font échouer la détection silencieusement — là où le tableau de bord reste vide, ce qui ressemble à s’y méprendre à « aucune attaque » — et affiche la correction exacte pour chacun. Il se termine avec un code non nul en cas de véritable échec, il est donc sûr de l’exécuter dans un CI ou une étape de déploiement.``` Threat Detection — health check
PASS Detection is enabled for this environment FAIL 'threat_logs' is missing confidence_label — EVERY threat is being discarded Run: php artisan vendor:publish --tag=threat-detection-migrations && php artisan migrate WARN 1 custom pattern(s) shadow a built-in: Localhost SSRF Your copy runs instead of the maintained one, so later fixes to it never reach you.
Ce qu'il couvre : la détection activée pour cet environnement ; chaque colonne dont
l'écrivain a besoin (une colonne manquante écarte **toutes** les menaces) ; les colonnes du
tableau de bord/API ; le tableau des règles d'exclusion ; si le middleware est réellement
connecté à une route ou à un groupe ; la configuration publiée qui précède cette version ;
les motifs personnalisés qui masquent ceux intégrés ; un pilote de cache incapable de
compter les DDoS ; et un tableau de bord ou une API laissé ouvert sans authentification.
**« J'ai testé mais `threat-detection:stats` affiche zéro menace » / « Les menaces ne sont pas stockées dans la base de données »**
Si le docteur a réussi, l'installation est correcte et le problème vient de la requête
de test elle-même. Trois choses qu'il ne peut pas vérifier pour vous :
| Vérification | Comment vérifier |
|-------|---------------|
| L'IP n'est pas sur liste blanche | Si vous avez ajouté `THREAT_DETECTION_WHITELISTED_IPS` à `.env`, supprimez-le pendant les tests |
| Utilisation d'une route existante | L'URL de test doit correspondre à une route réelle (par ex. `/`). Une erreur 404 signifie que le middleware ne s'est jamais exécuté |
| Cache de déduplication | Même IP + même type d'attaque est mis en cache pendant 5 minutes - essayez un type d'attaque différent |
> Exécuter `php artisan migrate` seul ne suffit jamais : les fichiers de migration se trouvent
> dans le package et doivent être publiés dans `database/migrations/` de votre application
> au préalable. Le docteur affiche la commande exacte lorsque c'est le problème.
**« L'API renvoie 401 Unauthorized »**
Voir [Authentification API](#api-authentication) ci-dessous.
**« Le tableau de bord affiche 404 »**
Le tableau de bord est désactivé par défaut. Ajoutez `THREAT_DETECTION_DASHBOARD=true` à `.env` et videz le cache des routes :```bash
php artisan route:clear
Fonctionnalités
- Plus de 150 modèles de détection - Injection SQL (UNION, DDL, DML, opérations sur fichiers), XSS (script, SVG, expression CSS), RCE, traversée de répertoires, SSRF, XXE, Log4Shell, injection NoSQL, injection de commandes (Linux + Windows), injection LDAP, injection XPath, SSTI, injection CRLF, désérialisation Java, et plus encore
- 83 signatures de bots/scanners - SQLMap, Nikto, Nmap, Burp Suite, FeroxBuster, FFUF, XSStrike, Dalfox, Netsparker, et plus de 70 autres signatures de scanners et de bots
- Détection des scrappers IA - GPTBot, ClaudeBot, ByteSpider, Common Crawl, et autres bots d'entraînement IA
- Détection des navigateurs headless - HeadlessChrome, PhantomJS, Selenium, Puppeteer, Playwright
- Suivi des sondes 404 - Détecte les sondes de reconnaissance visant des chemins vulnérables connus (
/wp-admin,/.env,/phpmyadmin,/actuator, etc.) avec plus de 50 chemins de sonde par défaut - Surveillance DDoS - Détection par seuil basé sur le taux avec fenêtres configurables
- Score de confiance - Chaque menace reçoit un score de confiance de 0 à 100 basé sur le nombre de modèles, le contexte et les signaux
- Résistance à l'évasion - Pipeline de normalisation neutralise l'insertion de commentaires SQL, le double encodage URL, l'encodage d'entités HTML, les échappements Unicode et les échappements hexadécimaux avant la correspondance des modèles
- Détection CVE - Shellshock (CVE-2014-6271), Spring4Shell (CVE-2022-22965), RCE PHPUnit (CVE-2017-9841), Drupalgeddon, Log4Shell
- Détection contextuelle - Les modèles trouvés dans les chaînes de requête obtiennent un score plus élevé que ceux présents dans le corps de la requête
- Analyse du corps de requête - Les corps de requête encodés en formulaire et en JSON (
application/json) sont tous deux inspectés - Champs sûrs - Exclure des champs de formulaire spécifiques de l'analyse (pour les éditeurs CMS, les champs de code, les champs de recherche)
- Signalement des faux positifs - Marquer les menaces comme faux positifs depuis le tableau de bord ; crée automatiquement des règles d'exclusion
- Trois modes de détection -
strict,balanced(par défaut) etrelaxed- sensibilité réglable - Suppression par chemin de contenu - Liste blanche des chemins CMS/blog pour supprimer les alertes faibles/moyennes provenant de contenu riche
- Détection PII - Modèles d'exposition de données sensibles (configurables par région)
- Enrichissement géographique - Identification du pays, de la ville, du FAI et du fournisseur cloud via une API gratuite
- Alertes Slack - Notifications en temps réel pour les menaces de haute sévérité (fonctionne sur Laravel 10 et 11+)
- Tableau de bord intégré - Tableau de bord Blade en mode sombre (Alpine.js + Tailwind CDN, aucune étape de compilation)
- Garde d'authentification du tableau de bord - Authentification configurable pour le tableau de bord et l'API (aucune, auth, rôle, ou basée sur IP)
- 15 points de terminaison API - API REST complète pour créer des tableaux de bord Vue/React/mobile personnalisés
- Export Fail2ban - Exporte les IP détectées au format compatible fail2ban ou en liste de blocage simple
- Export de liste de blocage - Exporte les IP au format deny nginx, deny Apache, CSV ou texte brut
- Export CSV - Export des journaux de menaces en un clic (jusqu'à 10 000 lignes)
- Analyse de corrélation - Détecte les attaques coordonnées et les campagnes d'attaque entre IP
- Performances optimisées - Chargement paresseux des modèles par catégorie (n'exécute les regex que pour les catégories d'attaque pertinentes), sortie anticipée pour les requêtes propres, court-circuit du navigateur UA (ignore plus de 70 vérifications pour les navigateurs normaux), recherche par hachage des chemins de sonde, insertions DB par lots, nombre maximal configurable de détections par requête
- Indépendant de la base de données - MySQL, PostgreSQL, SQLite, SQL Server
- Zéro configuration - Fonctionne immédiatement avec des valeurs par défaut sensées
- Sûr par conception - Le middleware capture ses propres erreurs. Si la détection échoue, votre application continue de fonctionner. Les requêtes ne sont jamais bloquées.
Configuration
Le paquet fonctionne sans aucune modification de .env. Toutes les valeurs ci-dessous sont facultatives - ajoutez-les uniquement si vous souhaitez remplacer les valeurs par défaut.```env
Enable/disable detection globally (default: true)
THREAT_DETECTION_ENABLED=true
Detection sensitivity (default: balanced)
Options: strict, balanced, relaxed
THREAT_DETECTION_MODE=balanced
Custom table name (default: threat_logs)
THREAT_DETECTION_TABLE=threat_logs
Your ISO 3166-1 alpha-2 country code (default: IN)
Drives the is_foreign flag on every enriched row — set this or every
non-Indian address is reported as foreign.
THREAT_DETECTION_HOME_COUNTRY=IN
Geo-enrichment provider used by threat-detection:enrich (default shown).
Cleartext HTTP because ip-api.com's free tier rejects HTTPS; point this at
an HTTPS endpoint if you hold a key. Enrichment is opt-in either way.
THREAT_DETECTION_GEO_ENDPOINT=http://ip-api.com/json
Dashboard URL path (default: threat-detection)
THREAT_DETECTION_DASHBOARD_PATH=threat-detection
API route prefix (default: api/threat-detection)
THREAT_DETECTION_API_PREFIX=api/threat-detection
Role required when the API guard is 'role' (default: admin)
THREAT_DETECTION_API_ROLE=admin
Allowed IPs when the API guard is 'ip'. Comma-separated, CIDR supported.
THREAT_DETECTION_API_IPS=127.0.0.1,10.0.0.0/8
Username shown on Slack alerts (default: ThreatBot)
THREAT_DETECTION_SLACK_USERNAME=ThreatBot
Whitelist IPs to skip detection entirely (default: empty)
Supports CIDR notation. Comma-separated.
THREAT_DETECTION_WHITELISTED_IPS=10.0.0.0/8,192.168.1.0/24
Static operator denylist read by ThreatDetection::isBlocklisted() (default: empty)
The package itself never blocks — see "Acting on the Data" for the
enforcement recipe. Supports CIDR. Whitelist wins on overlap.
THREAT_DETECTION_BLOCKLISTED_IPS=203.0.113.0/24,198.51.100.7
DDoS detection thresholds (defaults shown)
THREAT_DETECTION_DDOS_THRESHOLD=300
THREAT_DETECTION_DDOS_WINDOW=60
Minimum confidence score to log a threat (default: 0)
Threats below this score are silently ignored.
THREAT_DETECTION_MIN_CONFIDENCE=0
Slack notifications (disabled by default)
THREAT_DETECTION_NOTIFICATIONS=true
THREAT_DETECTION_SLACK_WEBHOOK=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
THREAT_DETECTION_SLACK_CHANNEL=#threat-alerts
Dashboard (disabled by default)
THREAT_DETECTION_DASHBOARD=true
API endpoints (enabled by default)
THREAT_DETECTION_API=true
API rate limiting (default: 60 requests per minute)
THREAT_DETECTION_API_THROTTLE=60,1
Queue support - offload DB writes to a queue (disabled by default).
OPTIONAL: only enable if your app already runs a queue worker. When false
(default), threats are written synchronously with a plain DB insert - no
Redis, no worker, nothing extra to run.
THREAT_DETECTION_QUEUE=false
THREAT_DETECTION_QUEUE_CONNECTION=redis
THREAT_DETECTION_QUEUE_NAME=default
Auto-purge old logs (disabled by default)
Requires Laravel scheduler to be running.
THREAT_DETECTION_RETENTION=false
THREAT_DETECTION_RETENTION_DAYS=90
404 probe tracking (enabled by default)
Detects bots hitting /wp-admin, /.env, /phpmyadmin, etc.
THREAT_DETECTION_PROBE_TRACKING=true
Max detections per request (default: 0 = unlimited)
Stop scanning after N pattern matches per request.
THREAT_DETECTION_MAX_DETECTIONS=0
Dashboard auth guard (default: none)
Options: none, auth, role, ip
THREAT_DETECTION_DASHBOARD_GUARD=none
THREAT_DETECTION_DASHBOARD_ROLE=admin
THREAT_DETECTION_DASHBOARD_IPS=127.0.0.1
API auth guard (default: none - uses existing middleware config)
THREAT_DETECTION_API_GUARD=none
### Modes de détection
| Mode | Seuil de confiance | Comportement |
|------|---------------------|----------|
| `strict` | 0 (journalise tout) | Tous les motifs actifs, seuils les plus bas. Détecte tout mais peut signaler du trafic légitime. |
| `balanced` | 10 | Par défaut. Scoring de confiance actif, seuils standard. Adapté à la plupart des applications. |
| `relaxed` | 40 | Seuls les motifs de haute sévérité déclenchent. Idéal pour les sites à fort contenu avec de fréquents faux positifs. |
### Environnements activés
Par défaut, la détection s'exécute dans `production`, `staging` et `local`. Pour modifier cela, publiez la configuration et éditez-la :```php
'enabled_environments' => ['production', 'staging', 'local'],
Pour désactiver la détection dans votre suite de tests, définissez APP_ENV=testing (non listé ci-dessus) ou ajoutez à votre phpunit.xml :```xml
### Référence de configuration
Publiez le fichier de configuration pour voir toutes les options disponibles :```bash
php artisan vendor:publish --tag=threat-detection-config
Sections de configuration clés : skip_paths (chemins à ignorer), only_paths (mode liste blanche), auth_paths (détection intelligente des routes de connexion), content_paths (suppression des alertes non critiques), safe_fields (exclusion de champs spécifiques de l’analyse), safe_paths (exclusion de champs sensible au chemin pour JSON imbriqué), probe_tracking (détection des sondes 404), context_weights (multiplicateurs de score), threat_levels (mappage des mots-clés de sévérité), api_route_filtering (suppression des alertes faibles/moyennes sur les routes API), queue (traitement asynchrone), retention (purge automatique), max_detections_per_request (plafond de performance), dashboard.guard / api.guard (mode d’authentification).
Liste blanche de routes (only_paths)
Si votre application possède de nombreuses routes mais que vous ne vous intéressez qu’à quelques-unes, utilisez only_paths pour analyser uniquement ces routes. Toutes les autres routes sont automatiquement ignorées — aucun surcoût middleware du tout.```php
// config/threat-detection.php
'only_paths' => [
'admin/',
'api/',
'login',
'register',
],
Laissez vide (par défaut) pour analyser toutes les routes (sous réserve de `skip_paths`). Lorsque les deux sont configurés, `only_paths` est vérifié en premier, puis `skip_paths` s’applique dans l’ensemble correspondant.
### Prise en charge des files d’attente
Par défaut, la journalisation des menaces s’effectue de manière synchrone dans le cycle de requête. Pour les applications à fort trafic, vous pouvez décharger les écritures en base de données et les notifications Slack vers une file d’attente :```env
THREAT_DETECTION_QUEUE=true
THREAT_DETECTION_QUEUE_CONNECTION=redis
THREAT_DETECTION_QUEUE_NAME=threat-logs
Ceci déclenche un job StoreThreatLog (3 nouvelles tentatives, backoff 10s/30s). La détection reste en temps réel — seul l'écriture est différée.
Purge automatique (politique de rétention)
Supprime automatiquement les anciens journaux de menaces selon un planning quotidien :```env THREAT_DETECTION_RETENTION=true THREAT_DETECTION_RETENTION_DAYS=90
Nécessite que le planificateur de Laravel soit en cours d'exécution (`php artisan schedule:run`). S'exécute quotidiennement à 02:00 via `threat-detection:purge`.
### Événement ThreatDetected
Chaque menace confirmée déclenche un événement `ThreatDetected` auquel vous pouvez vous abonner :```php
// app/Providers/EventServiceProvider.php
use JayAnta\ThreatDetection\Events\ThreatDetected;
protected $listen = [
ThreatDetected::class => [
YourCustomListener::class,
],
];
The event carries $threatLog (full DB row array), $ipAddress, and $threatLevel. Use it to trigger custom actions - send Telegram alerts, update a blocklist, feed a SIEM, etc.
DdosThresholdExceeded Event
When a client crosses the configured DDoS threshold (ddos.threshold requests within
ddos.window seconds), a DdosThresholdExceeded event is dispatched alongside the threat
log entry:```php
use JayAnta\ThreatDetection\Events\DdosThresholdExceeded;
protected $listen = [ DdosThresholdExceeded::class => [ YourFloodListener::class, ], ];
L'événement transporte `$ipAddress`, `$requestCount`, `$threshold` et `$windowSeconds`. Il est
limité à une fois par IP par fenêtre de déduplication (même limite que la ligne de journal), donc une inondation ne peut pas
submerger vos écouteurs. Utilisez-le pour les alertes ou pour alimenter un stockage de bannissement externe ; pour *refuser*
les clients dépassant le seuil, utilisez `ThreatDetection::isDdosThresholdExceeded($ip)` depuis votre propre
middleware à la place — voir [Agir sur les données](#acting-on-the-data-operator-side-blocking).
---
## Notifications Slack
Les alertes Slack sont désactivées par défaut. Pour les activer :```env
THREAT_DETECTION_NOTIFICATIONS=true
THREAT_DETECTION_SLACK_WEBHOOK=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
THREAT_DETECTION_SLACK_CHANNEL=#threat-alerts
Seules les menaces de haute sévérité déclenchent des notifications par défaut (configurable via notify_levels dans la configuration).
Laravel 10 : Utilise la classe de notification intégrée SlackMessage. Aucun paquet supplémentaire n'est nécessaire.
Laravel 11+ : Le canal Slack intégré a été supprimé. Le paquet détecte automatiquement cela et envoie des webhooks HTTP POST bruts vers votre URL Slack. Aucun paquet supplémentaire n'est nécessaire. Si vous préférez le canal de notification complet, installez :```bash composer require laravel/slack-notification-channel
---
## Tableau de bord
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/12500/fc7950bd0cc6323bcc2d62b03e31c99edc7450b0ef76f9cbfa5133b527b25269.png" alt="Tableau de bord de détection des menaces — statistiques, chronologie sur 7 jours, journal des menaces en direct, principales IP fautives et menaces par pays" width="100%">
</p>
Le package est livré avec un tableau de bord intégré en mode sombre (Alpine.js + Tailwind CDN — aucune étape de compilation requise).```
+-------------------------------------------------------------------------+
| Threat Detection Dashboard |
+-------------------------------------------------------------------------+
| Total: 847 | High: 23 | Med: 156 | Low: 668 | IPs: 94 |
+-------------------------------------------------------------------------+
| [Timeline Chart - 7 Day Stacked Bar] |
+-------------------------------------------------------------------------+
| Search: [___________] Level: [All] |
| Time IP Type Level Confidence Actions |
| Mar 2 14:02 185.220.101.4 SQL Injection HIGH 80% [FP] |
| Mar 2 13:58 45.33.32.156 XSS Script Tag HIGH 65% [FP] |
| Mar 2 13:45 192.168.1.10 Scanner: Nikto MED 35% [FP] |
+-------------------------------------------------------------------------+
| Top IPs | Threats by Country |
| 185.220.101.4 [23] | US 234 |
| 45.33.32.156 [18] | CN 156 |
| 103.152.220.1 [12] | RU 98 |
+-------------------------------------------------------------------------+
Activer le tableau de bord
Ajoutez à .env :```env
THREAT_DETECTION_DASHBOARD=true
Visitez : `http://your-app.test/threat-detection`
### Accéder au tableau de bord en développement local
Le tableau de bord utilise par défaut le middleware `['web', 'auth']`, les utilisateurs doivent donc être connectés. Si votre application n'a pas encore d'authentification, restreignez-le à votre propre machine à la place :```env
THREAT_DETECTION_DASHBOARD_GUARD=ip
THREAT_DETECTION_DASHBOARD_IPS=127.0.0.1
Toutes les options de garde, ainsi que la garde distincte sur les endpoints qui désactivent les détections, sont couvertes dans Authentification du tableau de bord et de l'API.
Si le tableau de bord affiche des données vides, la page s'est chargée mais ses appels API ont échoué. Voir Authentification de l'API.
Endpoints API
Le package fournit 15 endpoints REST pour créer des tableaux de bord personnalisés ou des intégrations.
Authentification de l'API
Les routes API utilisent le middleware auth:sanctum par défaut. Le package gère cela de manière transparente :
- Sanctum installé : L'API exige une authentification via des jetons Sanctum ou une authentification de session SPA.
- Sanctum NON installé : Le package détecte automatiquement que Sanctum est absent et revient à
['api']uniquement. L'API fonctionne sans authentification.
Si vous n'utilisez pas Sanctum mais souhaitez protéger votre API, vous avez deux options :
Option 1 - Utiliser la garde d'authentification intégrée :```env THREAT_DETECTION_API_GUARD=auth
**Option 2 - Modifier le middleware directement :**```php
// config/threat-detection.php
'api' => [
'enabled' => true,
'prefix' => 'api/threat-detection',
'middleware' => ['api', 'auth'], // or 'auth:your-guard'
],
Pour les tests locaux (si Sanctum bloque l'accès), modifiez temporairement :```php 'middleware' => ['api'], // remove 'auth:sanctum'
> Restaurez l'authentification avant le déploiement en production.
### Référence des points de terminaison
| Méthode | Point de terminaison | Description |
|---------|----------------------|-------------|
| GET | `/api/threat-detection/threats` | Liste des menaces (paginée, filtrable) |
| GET | `/api/threat-detection/threats/{id}` | Détails d'une menace unique |
| POST | `/api/threat-detection/threats/{id}/false-positive` | Marquer une menace comme faux positif |
| GET | `/api/threat-detection/stats` | Statistiques globales |
| GET | `/api/threat-detection/summary` | Répartition détaillée par type, niveau, IP |
| GET | `/api/threat-detection/live-count` | Menaces de la dernière heure |
| GET | `/api/threat-detection/by-country` | Regroupées par pays |
| GET | `/api/threat-detection/by-cloud-provider` | Regroupées par fournisseur cloud |
| GET | `/api/threat-detection/top-ips` | Principales IP fautives |
| GET | `/api/threat-detection/timeline` | Chronologie des menaces (pour graphiques) |
| GET | `/api/threat-detection/ip-stats?ip=x.x.x.x` | Statistiques pour une IP spécifique |
| GET | `/api/threat-detection/correlation` | Analyse de corrélation |
| GET | `/api/threat-detection/export` | Export en CSV |
| GET | `/api/threat-detection/exclusion-rules` | Liste des règles d'exclusion |
| DELETE | `/api/threat-detection/exclusion-rules/{id}` | Supprimer une règle d'exclusion |
### Paramètres de requête pour `/threats`
| Paramètre | Description |
|-----------|-------------|
| `keyword` | Recherche dans IP, URL, type |
| `ip` | Filtrer par adresse IP |
| `level` | Filtrer par niveau de menace (`high`, `medium`, `low`) |
| `type` | Filtrer par type de menace |
| `country` | Filtrer par code pays |
| `is_foreign` | Filtrer les IP étrangères (`true`/`false`) |
| `cloud_provider` | Filtrer par fournisseur cloud |
| `is_false_positive` | Filtrer par statut de faux positif (`true`/`false`) |
| `date_from` / `date_to` | Filtre de plage de dates |
| `per_page` | Éléments par page (défaut : 20, max : 100) |
### Exemple de réponse API
**GET `/api/threat-detection/stats` :**```json
{
"success": true,
"data": {
"total_threats": 847,
"high_severity": 23,
"medium_severity": 156,
"low_severity": 668,
"unique_ips": 94,
"foreign_ips": 67,
"cloud_attacks": 12,
"today": 34,
"last_hour": 5
}
}
Building Custom Frontends
Vue.js :```javascript async mounted() { const response = await fetch('/api/threat-detection/stats'); this.stats = await response.json();
const threats = await fetch('/api/threat-detection/threats?per_page=20');
this.threats = await threats.json();
}
**React :**```jsx
useEffect(() => {
fetch('/api/threat-detection/stats')
.then(res => res.json())
.then(data => setStats(data));
}, []);
Si votre API utilise
auth:sanctum, incluez les en-têtes d'authentification ou configurez l'authentification SPA Sanctum pour les requêtes basées sur les cookies.
Commandes Artisan```bash
Check that detection is installed, wired up and actually recording.
Exits non-zero on a real failure, so it works in CI or a deploy step.
php artisan threat-detection:doctor
View threat stats summary in the terminal
php artisan threat-detection:stats
Enrich existing logs with geo-data (country, city, ISP, cloud provider)
Uses the free ip-api.com service (rate-limited to 45 req/min, auto-throttled)
php artisan threat-detection:enrich --days=7
Purge old logs to keep the database clean
php artisan threat-detection:purge --days=30
Export threat IPs for fail2ban (pipe to file or run directly)
php artisan threat-detection:export-fail2ban --level=high --since=24h --min-hits=5 php artisan threat-detection:export-fail2ban --format=plain > /tmp/banlist.txt
Export blocklist in various formats
php artisan threat-detection:export-blocklist --format=nginx > /etc/nginx/blocklist.conf php artisan threat-detection:export-blocklist --format=apache > .htaccess-deny php artisan threat-detection:export-blocklist --format=csv --since=7d
---
## Agir sur les données (blocage côté opérateur)
Le package ne bloque jamais une requête — c'est son identité, pas un défaut. Les exports ci-dessus
alimentent les couches d'application que vous exécutez déjà (fail2ban, nginx, un WAF en périphérie). Mais certains déploiements
ne disposent d'aucune couche de ce type à alimenter — hébergement mutualisé, PaaS, conteneurs derrière un équilibreur de charge que vous
ne contrôlez pas. Pour ceux-là, le package expose ses *décisions* sous forme d'helpers, et vous écrivez vous-même
le middleware d'application. Même architecture que les exports : **nous fournissons
l'intelligence, vous fournissez le refus.**```php
// app/Http/Middleware/EnforceThreatDecisions.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use JayAnta\ThreatDetection\Facades\ThreatDetection;
class EnforceThreatDecisions
{
public function handle(Request $request, Closure $next)
{
$ip = (string) $request->ip();
// Static operator denylist (config: blocklisted_ips).
// CIDR supported; whitelisted_ips wins on overlap.
if (ThreatDetection::isBlocklisted($ip)) {
abort(403);
}
// Volumetric flood: refuse over-threshold clients until the window resets.
if (ThreatDetection::isDdosThresholdExceeded($ip)) {
return response('Too Many Requests', 429, [
'Retry-After' => (string) config('threat-detection.ddos.window', 60),
]);
}
return $next($request);
}
}
Avant d’appliquer une restriction sur IP, configurez
TrustProxies.Tout ce qui précède repose sur
$request->ip(). Derrière un équilibreur de charge, un CDN ou un proxy inverse, cela ne renvoie l’IP client que si Laravel sait quels proxys il doit approuver. Sinon, deux problèmes surviennent à la fois : chaque requête semble provenir du proxy, donc une entrée de liste de blocage bloque tout votre trafic ou rien du tout — et pire, si l’application approuve un en-tête transféré qu’elle ne devrait pas, un attaquant définitX-Forwarded-Foret contourne directement la liste de blocage.Cela compte ici plus que pour
whitelisted_ips. Une correspondance erronée dans la liste blanche signifie seulement que le package analyse une requête qu’il aurait pu ignorer : c’est un échec sans danger. Une liste de blocage utilisée pour refuser le trafic échoue ouvertement — vous croyez qu’une adresse est bloquée alors qu’elle ne l’est pas. Vérifiezapp/Http/Middleware/TrustProxies.php(ou l’appeltrustProxiesdansbootstrap/app.phpsur Laravel 11+) avant de vous fier à l’un ou l’autre de ces outils pour l’application des restrictions.
Enregistrez-le globalement (avant le middleware de détection convient — les outils lisent la configuration et le cache, ils ne dépendent pas de l’ordre des middlewares) :```php // bootstrap/app.php (Laravel 11+) ->withMiddleware(function ($middleware) { $middleware->prepend(\App\Http\Middleware\EnforceThreatDecisions::class); })
Les helpers :
| Helper | Retourne | Basé sur |
|---|---|---|
| `ThreatDetection::isBlocklisted($ip)` | `bool` | config `blocklisted_ips` (CIDR via `IpUtils` ; la liste blanche l'emporte) |
| `ThreatDetection::isWhitelisted($ip)` | `bool` | config `whitelisted_ips` |
| `ThreatDetection::ddosRequestCount($ip)` | `int` | le compteur d'inondation que le middleware de détection maintient |
| `ThreatDetection::isDdosThresholdExceeded($ip)` | `bool` | ce compteur par rapport à `ddos.threshold` |
Remarques :
- **La liste de blocage est statique et maintenue par l'opérateur.** Rien dans le package ne l'ajoute jamais — il exécute la même décision qu'une prison fail2ban (« J'ai lu le tableau de bord ; ce /24 est hostile »), simplement dans l'application.
- Le compteur DDoS ne compte que les requêtes qui ont atteint la détection (`skip_paths`, IP de la liste blanche et environnements désactivés ne sont jamais comptés), et reste à 0 sur les pilotes de cache où la détection DDoS est désactivée (`file`, `database`, `null`).
- Lorsqu'un client dépasse le seuil, un événement [`DdosThresholdExceeded`](#ddosthresholdexceeded-event) est également déclenché — utile pour les alertes ou pour alimenter une liste de bannissement externe. N'appelez pas `abort()` depuis l'écouteur, cependant : les écouteurs s'exécutent dans le `try/catch` à échec ouvert du middleware de détection, donc le refus doit se trouver dans votre propre middleware comme ci-dessus.
---
## Suivi des sondes 404
Le package détecte les sondes de reconnaissance — des bots qui frappent des chemins vulnérables connus comme `/wp-admin`, `/.env` ou `/phpmyadmin` sur votre site non-WordPress, non-phpMyAdmin. Ceux-ci ne contiennent aucune charge utile malveillante ; le chemin lui-même est le signal.
Journalisés avec une balise de type `[probe]`, séparée de la détection basée sur les charges utiles. Si une requête de sonde contient également une charge utile malveillante, les deux sont journalisées indépendamment.
Activé par défaut avec plus de 50 chemins de sondes. Personnalisez dans `config/threat-detection.php` :```php
'probe_tracking' => [
'enabled' => true,
'default_level' => 'medium',
'paths' => [
'/wp-admin' => 'WordPress Admin',
'/wp-admin/*' => 'WordPress Admin',
'/.env' => 'Environment File',
'/phpmyadmin' => 'phpMyAdmin',
'/actuator/*' => 'Spring Actuator',
// Add your own probe paths...
],
],
Désactiver avec THREAT_DETECTION_PROBE_TRACKING=false.
Champs sûrs (réduction des faux positifs)
Si des champs de formulaire spécifiques contiennent légitimement du HTML, des mots-clés SQL ou du code (par exemple, des éditeurs CMS, des champs de saisie d'extraits de code), vous pouvez les exclure de l'analyse :```php // config/threat-detection.php 'safe_fields' => ['content', 'body', 'html', 'description', 'code'],
Fields listed here are stripped from query params and the request body - both form-encoded and JSON (`application/json`) - before detection runs. Other fields on the same request are still fully scanned.
### Safe Paths (path-aware, for nested JSON APIs)
`safe_fields` matches a key name **anywhere** it appears. For nested JSON APIs that's often too broad — you may want to exempt one specific field's value without exempting that key everywhere. Use `safe_paths`, which matches by dot-notation **path** and supports `fnmatch` wildcards:```php
// config/threat-detection.php
'safe_paths' => ['search.query', 'filters.*.value'],
Par exemple, search.query exempte la valeur de {"search": {"query": "..."}} (une zone de recherche dont le texte contient légitimement des mots comme SELECT), tandis qu'un champ query ailleurs dans la requête est toujours analysé. Tout ce qui n'est pas listé est analysé exactement comme avant.
Validateurs post-correspondance (réduction des faux positifs basée sur la somme de contrôle)
Une regex seule ne peut pas exprimer toutes les contraintes : n'importe quelle séquence de 12 chiffres correspond au motif Aadhaar, mais un vrai numéro Aadhaar passe également la somme de contrôle Verhoeff. Associez un libellé de motif (par défaut ou personnalisé) à un validateur nommé, et une correspondance regex ne compte comme détection que lorsqu'au moins une valeur correspondante le valide :```php // config/threat-detection.php 'pattern_validators' => [ 'Aadhaar Number Detected' => 'verhoeff', // shipped default ],
Available validators:
| Validator | Checksum | Typical use |
|------------|----------|-------------|
| `verhoeff` | Verhoeff | Aadhaar numbers |
| `luhn` | Luhn | Credit/debit card numbers |
With the shipped mapping, timestamps, order ids and barcodes that happen to be 12 digits long are no longer logged as PII — while genuine Aadhaar numbers still are. If several values match and only one passes the checksum, the detection still fires: a real number among noise is still a leak.
Pair a validator with your own pattern for checksum-gated card detection:```php
'custom_patterns' => ['/\b(?:\d[ -]?){13,19}\b/' => 'Card Number Detected'],
'pattern_validators' => ['Card Number Detected' => 'luhn'],
Un nom de validateur inconnu échoue ouvertement — la correspondance est comptée sans validation et un avertissement est journalisé une seule fois — donc une faute de frappe ne peut jamais désactiver silencieusement un motif de détection. Les configurations publiées avant cette fonctionnalité ne possèdent tout simplement pas la clé et conservent leur comportement actuel exact.
Rédaction (Détecter n'est pas stocker)
Détecter des données sensibles signifiait autrefois les stocker. Un formulaire de profil contenant un numéro de mobile, un PAN et un compte bancaire déclencherait trois motifs PII, et chacune des trois lignes écrites conservait l'intégralité du corps de la requête mot pour mot — retenu pendant toute la période de conservation, lisible par quiconque ayant accès au tableau de bord ou à la base de données. Une valeur dans une chaîne de requête atterrissait également dans la colonne url. Le détecteur devenait une seconde copie concentrée de ce dont il vous avertit exactement.
Activé par défaut depuis la v1.7.0. Lorsqu'un motif dont le libellé est répertorié se déclenche, la valeur qu'il a détectée est masquée dans la charge utile stockée et l'URL :``` BODY: {"name":"Jane Doe","mobile":"[REDACTED]","pan":"[REDACTED]","bank_account":"[REDACTED]"}
L'alerte, le point de terminaison, les noms de champs et l'IP attaquante sont tous conservés — seule la valeur est supprimée. La rédaction s'exécute *après* la détection, donc rien n'est manqué.```php
// config/threat-detection.php
'redact' => [
'enabled' => env('THREAT_DETECTION_REDACT', true),
'mask' => '[REDACTED]',
'labels' => ['Aadhaar Number Detected', 'PAN Number Detected', /* ... */],
],
Attack payloads are délibérément laissés intacts - une chaîne d'injection est une preuve, pas un secret, et la masquer détruirait l'investigation. Seuls les libellés que vous listez sont modifiés.
Cela ne remplace pas Safe Fields. Ceux-ci empêchent un champ d'être scanné ; la rédaction vous permet de continuer à scanner et d'arrêter le stockage. Définissez
THREAT_DETECTION_REDACT=falsesi vous avez besoin des payloads complets pour la forensique.
Authentification du tableau de bord et de l'API
Le tableau de bord et l'API prennent en charge des gardes d'authentification configurables via .env :```env
Options: none (default), auth, role, ip
THREAT_DETECTION_DASHBOARD_GUARD=auth
For role-based guard (Spatie compatible):
THREAT_DETECTION_DASHBOARD_GUARD=role THREAT_DETECTION_DASHBOARD_ROLE=admin
For IP-based guard:
THREAT_DETECTION_DASHBOARD_GUARD=ip THREAT_DETECTION_DASHBOARD_IPS=127.0.0.1,10.0.0.0/8
Les mêmes options sont disponibles pour les routes API avec `THREAT_DETECTION_API_GUARD`.
Lorsque `guard=none` (par défaut), le package journalise un avertissement une fois par jour pour vous rappeler de configurer l'authentification.
La protection **échoue en mode fermé** : une valeur de garde non reconnue (par exemple une faute de frappe) est refusée avec un 403 et un avertissement journalisé plutôt que d'accorder silencieusement l'accès, et `guard=role` refuse (avec un avertissement) lorsque le modèle utilisateur authentifié ne possède pas de méthode `hasRole()`.
### Désactiver une détection nécessite plus qu'un accès en lecture
Marquer une menace comme faux positif et supprimer une règle d'exclusion désactivent tous deux un type de détection pour tout le monde, ce qui constitue un privilège différent de la lecture du journal. Ces deux points de terminaison sont vérifiés par une protection distincte :```env
# Options: none, auth, role, ip. Default: role
THREAT_DETECTION_API_WRITE_GUARD=role
Il s'applique uniquement à ces routes, donc la lecture et le tableau de bord se comportent exactement comme le spécifie THREAT_DETECTION_API_GUARD. Sans cela, tout utilisateur authentifié de votre application pourrait désactiver une détection.
Si votre modèle utilisateur n'a pas de hasRole(), utilisez =auth. Pour restaurer le comportement d'avant la version 1.7.0 où tout utilisateur authentifié pouvait désactiver les détections, utilisez =none - threat-detection:doctor émettra un avertissement tant que cela est défini.
Note Tableau de bord ↔ API : le tableau de bord intégré récupère ses données depuis les routes API en utilisant le cookie de session du navigateur. Si vos routes API sont protégées avec
auth:sanctum, configurez l'authentification Sanctum stateful/SPA (ou pointez le tableau de bord vers un garde authentifié par cookie) afin que ces appels AJAX soient autorisés - sinon le tableau de bord s'affiche vide.
Modèles personnalisés
Ajoutez vos propres modèles d'expression régulière de détection dans config/threat-detection.php :```php
'custom_patterns' => [
'/your-regex-here/i' => 'Your Threat Label',
],
**Exemple - détecter une sonde d'endpoint admin personnalisée :**```php
'/\/my-admin-panel/i' => 'Custom Admin Panel Probe',
Forme tableau (options par motif)
En plus de la forme classique en chaîne de caractères, la valeur d'un motif peut être un tableau pour un contrôle complet :```php 'custom_patterns' => [ '/\b(?:\d[ -]?){13,19}\b/' => [ 'label' => 'Card Number Detected', // required 'level' => 'high', // low|medium|high — overrides keyword derivation 'contexts' => ['query', 'body'], // query|body|headers — default: all segments 'validator' => 'luhn', // post-match checksum, wins over pattern_validators ], ],
- **`level`** définit directement le niveau de menace au lieu de le dériver des mots-clés `threat_levels` dans le libellé.
- **`contexts`** restreint l'analyse à des segments de requête spécifiques — par exemple, un motif de carte qui n'a de sens que dans le corps cesse de correspondre aux séquences de chiffres dans les en-têtes.
- **`validator`** nomme une vérification post-correspondance intégrée (voir [Validateurs post-correspondance](#post-match-validators-checksum-aware-false-positive-reduction)) ; il a priorité sur la correspondance de libellés `pattern_validators`.
Les entrées de chaînes et de tableaux se mélangent librement dans la même configuration. Les options malformées **échouent ouvertement** — le motif analyse toujours, sans restriction, et un avertissement est consigné — de sorte qu'une erreur de configuration ne peut jamais désactiver ou restreindre silencieusement une détection.
> **Remarque :** Les chemins de sondage courants comme `/wp-login.php`, `/.env`, `/phpmyadmin` sont désormais gérés automatiquement par la fonctionnalité [Suivi des sondes 404](#404-probe-tracking). Vous n'avez pas besoin de motifs personnalisés pour ceux-ci.
Le niveau de menace pour chaque motif est déterminé automatiquement en faisant correspondre les mots-clés du libellé avec la configuration `threat_levels` :```php
'threat_levels' => [
'high' => ['XSS', 'SQL Injection', 'SQL DDL', 'SQL DML', 'SQL File', 'SQL Hex', 'RCE', ..., 'Shellshock', 'Spring4Shell', 'PowerShell', 'CRLF', 'Null Byte', 'SSTI', 'Java', 'LDAP', 'XPath', 'PHP assert', ...],
'medium' => ['Directory Traversal', 'LFI', 'SSRF', 'Sensitive', 'Config', ..., 'Open Redirect', 'LF Injection', 'GraphQL', 'Spring Boot Actuator', ...],
'low' => ['User-Agent', 'JS Redirect', 'SEO Bot', 'Empty', 'Rate', 'Command-line Downloader', 'DNS Rebinding'],
],
Si l'étiquette ne correspond à aucun mot-clé, la menace est par défaut de sévérité low.
Les motifs d'expression régulière invalides sont automatiquement ignorés et consignés comme avertissements — ils ne feront pas planter votre application.
Utilisation de la façade
Pour un accès programmatique aux données de menace en dehors du middleware :```php use JayAnta\ThreatDetection\Facades\ThreatDetection;
// Get attack statistics for a specific IP $stats = ThreatDetection::getIpStatistics('192.168.1.1');
// Detect coordinated attacks (multiple IPs targeting same URL within 15 minutes) $attacks = ThreatDetection::detectCoordinatedAttacks(15, 3);
// Detect attack campaigns (same threat type from 5+ IPs in last 24 hours) $campaigns = ThreatDetection::detectAttackCampaigns(24);
// Get a summary of all correlation data $summary = ThreatDetection::getCorrelationSummary();
// Operator-side decision helpers (see "Acting on the Data") $blocked = ThreatDetection::isBlocklisted('203.0.113.7'); // static denylist, CIDR, whitelist wins $trusted = ThreatDetection::isWhitelisted('10.0.0.5'); $count = ThreatDetection::ddosRequestCount('203.0.113.7'); // requests in the current DDoS window $flooded = ThreatDetection::isDdosThresholdExceeded('203.0.113.7');
## Passage en production
Le paquet est passif par conception - il ne bloque, ne rejette ni ne modifie jamais une requête, et le middleware de détection enveloppe tout son corps dans un `try/catch`, de sorte qu'un échec de détection ne peut jamais casser votre application. Il est livré avec des valeurs par défaut sensées et ne nécessite aucun service externe pour fonctionner. Avant de passer en production, cette courte liste de contrôle mérite un coup d'œil :
1. **Protégez le tableau de bord et l'API.** Les deux utilisent par défaut `guard = none` pour un premier lancement sans configuration, et journalisent un avertissement quotidien tant qu'ils ne sont pas protégés. Avant la production, définissez un garde - `THREAT_DETECTION_DASHBOARD_GUARD` et `THREAT_DETECTION_API_GUARD` (`auth`, `role`, ou `ip`). Une valeur non reconnue ou un garde `role` sur un modèle utilisateur sans `hasRole()` échoue désormais en mode **fermé** (403), de sorte qu'une faute de frappe n'exposera pas silencieusement des données. La désactivation d'une détection est contrôlée séparément par `THREAT_DETECTION_API_WRITE_GUARD`, qui utilise par défaut `role`. Voir [Authentification du tableau de bord et de l'API](#dashboard-and-api-authentication).
2. **Exécutez les migrations** (`vendor:publish --tag=threat-detection-migrations && migrate`). La republication est sûre - les migrations déjà publiées sont ignorées.
3. **Choisissez un mode de détection.** `balanced` (par défaut) convient à la plupart des applications ; utilisez `relaxed` pour les sites riches en contenu, `strict` pour les surfaces à haute sécurité. Ajustez avec `content_paths`, `safe_fields`, et `min_confidence` - voir [Réduction des faux positifs](#reducing-false-positives).
4. **Examinez les modèles régionaux de PII / personnalisés.** Les valeurs par défaut sont centrées sur l'Inde (Aadhaar, PAN, IFSC) et les modèles numériques larges (par exemple, compte bancaire) peuvent correspondre à de longs identifiants numériques en dehors des routes d'authentification. Remplacez ou réduisez `custom_patterns` pour votre région et votre application, et ajoutez les routes à contenu lourd à `auth_paths` / `content_paths`.
5. **Activez la rétention** si vous attendez du volume : `THREAT_DETECTION_RETENTION=true` (purge automatique via le planificateur). Nécessite que le planificateur de Laravel (`schedule:run`) soit piloté par cron.
6. **Extras optionnels, tous désactivés par défaut :** alertes Slack (`THREAT_DETECTION_NOTIFICATIONS`), enrichissement géographique (`php artisan threat-detection:enrich` - la seule fonctionnalité qui effectue un appel sortant, vers le service gratuit ip-api.com), et écritures en file d'attente (`THREAT_DETECTION_QUEUE` - activez uniquement si vous exécutez déjà un worker de file d'attente ; sinon, les écritures sont synchrones et ne nécessitent pas Redis).
Aucun Redis, aucun worker de file d'attente et aucun appel réseau sortant ne sont requis pour la détection et la journalisation de base.
---
## Réduction des faux positifs
Le paquet fournit plusieurs outils pour réduire les faux positifs. Utilisez celui qui correspond à votre situation :
### Champs sûrs et chemins sûrs
Excluez un champ de l'analyse entièrement, soit par nom partout (`safe_fields`), soit par chemin en notation pointée pour le JSON imbriqué (`safe_paths`). L'approche la plus simple, et la plus brutale - le champ est ignoré, donc aucune détection n'est exécutée dessus.
Détails complets et exemples : [Champs sûrs](#safe-fields-false-positive-reduction).
### Suppression des chemins de contenu
Si vous avez des éditeurs CMS, des formulaires de billets de blog ou des sections de commentaires où les utilisateurs soumettent du contenu riche, ces chemins déclenchent souvent des faux positifs (par exemple, un billet de blog contenant des exemples de code `<script>`). Ajoutez ces chemins pour supprimer les alertes faibles/moyennes :```php
// config/threat-detection.php
'content_paths' => [
'admin/posts/*',
'admin/pages/*',
'blog/*/edit',
'comments',
],
Sur ces chemins, seules les menaces de sévérité élevée sont journalisées.
Signalement des faux positifs
Cliquez sur le bouton FP sur n'importe quelle menace dans le tableau de bord pour la marquer comme faux positif. Cela :
- Marque la menace avec
is_false_positive = true - Crée automatiquement une règle d'exclusion afin que les menaces similaires provenant de la même URL/type soient supprimées à l'avenir
Gérez les règles d'exclusion via l'API :```bash GET /api/threat-detection/exclusion-rules DELETE /api/threat-detection/exclusion-rules/{id}
### Score de confiance
Chaque menace reçoit un score de confiance (0-100) basé sur :
- Le nombre de correspondances de motifs dans la même requête
- La gravité du motif correspondant
- L'endroit où le motif a été trouvé (chaîne de requête > en-têtes > corps)
- Si le user-agent correspond à un outil d'attaque connu
- Le mode de détection actuel
Les menaces en dessous du seuil de confiance pour votre mode de détection ne sont pas journalisées (voir [Modes de détection](#detection-modes)).
---
## Types d'attaques détectés
| Catégorie | Exemples |
|----------|---------|
| **Injection SQL** | UNION, booléen, basé sur le temps, encodage CHAR, DDL (DROP/ALTER/CREATE), DML (INSERT/UPDATE/DELETE), opérations sur fichiers (INTO OUTFILE, LOAD_FILE), énumération ORDER BY, chaînes hexadécimales, UNHEX |
| **Injection NoSQL** | Opérateurs MongoDB $ne, $gt, $regex, $where |
| **XSS** | Balises script, gestionnaires d'événements SVG (`<svg onload=`), gestionnaires d'événements HTML (`<body onload=`, `<img onerror=`), expressions CSS, URI JavaScript, manipulation DOM |
| **Exécution de code** | Fonctions shell RCE, désérialisation PHP, désérialisation Java (octets magiques base64 + hex), injection de templates (Blade, JSP, ASP, Jinja2, Velocity), eval(), décodage base64, assert() PHP, create_function(), preg_replace /e |
| **SSTI** | Sondes mathématiques (`{{7*7}}`), import/config Jinja2, templates Velocity, Expression Language |
| **Injection de commandes** | Linux (fonctions shell, chaînes de commandes, curl, wget, nc), Windows (cmd.exe, PowerShell, wscript, cscript, net user) |
| **Accès aux fichiers** | Traversée de répertoires, protocoles LFI/RFI, sondes de fichiers sensibles (.env, .git, composer.json) |
| **SSRF** | Localhost (127.0.0.1, 0.0.0.0, ::1), métadonnées AWS/GCP, IP privées, localhost encodé hexadécimal/décimal, rebinding DNS (xip.io, nip.io, sslip.io) |
| **Injection LDAP** | Manipulation de filtres LDAP, injection OR |
| **Injection XPath** | Sélecteurs d'attributs, fonctions XPath (contains, substring) |
| **Injection CRLF / En-têtes** | CRLF encodé URL (`%0d%0a`), injection LF, injection d'octets nuls |
| **Attaques de protocole** | Contrebande de requêtes HTTP (CL+TE), injection SSI |
| **Exploits CVE** | Shellshock (CVE-2014-6271), Spring4Shell (CVE-2022-22965), RCE PHPUnit (CVE-2017-9841), Drupalgeddon, Log4Shell |
| **Suivi de sondes** | WordPress (`/wp-admin`, `/wp-login.php`), fichiers de configuration (`/.env`, `/.git`), outils de base de données (`/phpmyadmin`), sondes technologiques (`.asp`, `.jsp`), Spring actuator, Swagger/docs API - plus de 50 chemins |
| **Scanners** | SQLMap, Nikto, Nmap, Burp Suite, FeroxBuster, FFUF, XSStrike, Dalfox, Netsparker, Qualys, Nuclei, et plus de 20 autres (53 au total) |
| **Scrapers IA** | GPTBot, ClaudeBot, ChatGPT, ByteSpider, Cohere, Common Crawl |
| **Navigateurs sans tête** | HeadlessChrome, PhantomJS, Selenium, Puppeteer, Playwright |
| **Robots** | Scripts Python, clients HTTP Go, cURL, wget, AhrefsBot, SEMRushBot, user agents vides |
| **Authentification** | Détection de force brute, fuites de jetons, exposition de mots de passe, exposition d'ID de session |
| **DDoS** | Détection de requêtes excessives basée sur le taux |
| **Évitement** | Insertion de commentaires SQL, double encodage URL, encodage d'entités HTML, échappements Unicode, IIS Unicode, échappements hexadécimaux |
| **Autre** | Introspection GraphQL, pollution de prototype, redirection ouverte, XXE, webshells, minage de crypto, détection de PII |
---
## Exécution de la suite de tests```bash
composer test
Le paquet inclut 335 tests (856 assertions) couvrant les modèles de détection, le comportement du middleware, les points de terminaison API, la notation de confiance, les règles d'exclusion, la détection DDoS, la résistance à l'évasion, les modèles CVE, l'injection LDAP/XPath/SSTI, la détection de robots/scanners, le suivi des sondes, les commandes d'exportation, l'authentification du tableau de bord, les champs sûrs, les optimisations de performance et la vérification complète HTTP-vers-DB.
Licence
Licence MIT. Voir LICENSE pour plus de détails.
Contribution
Les contributions sont les bienvenues ! Veuillez soumettre une Pull Request.
Crédits
- Jay Anta - auteur et mainteneur
- David van der Tuijn - prise en charge de Laravel 13
- Tous les contributeurs