
Automatisez le blocage des mauvais bots qui accèdent à votre serveur
Une TUI, une interface web et une CLI qui vous aident à configurer votre serveur pour bloquer les mauvais bots sans vous cacher derrière un CDN.
Il fonctionne aux côtés de NGINX et de votre pare-feu existant (iptables ou nftables), sur deux plans distincts :
L'application fait de son mieux pour ne pas vous verrouiller hors du serveur, mais vous l'utilisez à vos propres risques. Et notez qu'elle est sous licence AGPL, donc si vous l'utilisez à des fins commerciales, assurez-vous de respecter la lettre de la licence.
Depuis crates.io :``` cargo install stop-bots
Ou compiler depuis un checkout :```
cargo install --path .
Un binaire Linux x86_64 précompilé est disponible sur les releases GitHub.
Nécessite Rust 1.88 ou plus récent pour compiler. Linux uniquement en pratique : il appelle
systemctl, nginx -t et nft/iptables, donc bien qu'il compile ailleurs, il n'y sera
guère utile.
Lancez le binaire sans argument pour démarrer la TUI, stop-bots web pour les mêmes écrans dans
un navigateur (voir L'interface web), ou consultez stop-bots --help pour la liste complète des
sous-commandes CLI. La TUI et la CLI peuvent être utilisées ensemble. Configurez tout dans la TUI et
utilisez ensuite la CLI dans un crontab pour maintenir les règles à jour.
Vous pouvez quitter l'application, ou revenir en arrière depuis un popup/sous-menu, avec 'q' ou Échap.
Vous pouvez basculer entre le thème sombre et clair avec 't'. L'application tentera de détecter automatiquement le thème, mais pour certaines combinaisons de terminal et de multiplexeur, les informations disponibles ne suffisent pas pour faire le bon choix.
1–4 (ou d/b/s/p) sautent directement à un écran ; Gauche/Droite, ou leurs alias vim h/l,
les parcourent un par un ; ? affiche à tout moment une référence complète des raccourcis clavier, et :
ouvre une palette de commandes listant chaque action par son nom. Tab / Shift+Tab déplacent toujours entre
les panneaux de l'écran courant, jamais entre les écrans.
Le Dashboard possède tout ce qui finit dans le script de pare-feu ; les paramètres de site possèdent tout ce qui finit dans la configuration NGINX. Cette séparation détermine où vit chaque paramètre donné.
Up/Down circulent entre les trois listes ; m change le mode géo. Appuyez sur F pour générer les
règles de pare-feu actuelles dans un script — le
popup dispose aussi d'un interrupteur « apply after writing » (Espace) pour l'appliquer réellement
immédiatement, au lieu de l'appliquer à la main ensuite. Trois autres touches agissent sur l'ensemble de
l'hôte : u télécharge toutes les listes, a applique les deux plans (NGINX, puis le pare-feu), et
w place cette console derrière NGINX — les trois mêmes que le navigateur propose sous forme de boutons et d'un
panneau.Tab pour le cibler) contenant les choix
à l'échelle de l'hôte qui façonnent la configuration générée — ce qu'une requête bloquée reçoit en retour (voir ci-dessous),
s'il faut servir un robots.txt généré, et la limitation de débit — au-dessus de chaque site NGINX
découvert sur disque, chacun avec un statut en direct « up to date / stale / not found » et des actions pour
appliquer la politique courante à un site ou à tous. Modifier l'un de ces paramètres fait passer
chaque site appliqué à STALE, ce qui est votre signal pour réappliquer. Ouvrir un site vous permet de
remplacer sa politique de catégorie/bot, d'activer l'une des six règles de forme de requête, et de lister
les chemins exemptés de blocage.NOT BLOCKED/BLOCKED (affiché en rouge). Tab/Shift+Tab changent lequel des deux panneaux
Up/Down affecte ; f fait défiler un filtre partagé (tous / non bloqués uniquement / bloqués uniquement) ;
Enter bloque la ligne NOT BLOCKED sélectionnée, ou la débloque si elle est déjà BLOCKED.
i inspecte l'adresse sélectionnée : lesquels des flux de réputation la listent, si elle se trouve
dans une plage de crawler publiée (ce qui distingue un vrai Googlebot d'un user
agent qui prétend simplement l'être), à quel pays elle appartient, et quels comptes elle a tenté de
utiliser pour se connecter. Tout cela à partir de listes que cet hôte a déjà téléchargées — il n'y a pas de reverse
DNS ni de recherche whois ici, car un enregistrement PTR est écrit par celui qui détient l'adresse
et serait un texte fourni par l'attaquant qui se lit comme faisant autorité.Bot settings, où vivent chaque source de liste et chaque bot individuel :
Site settings, où les choix NGINX à l'échelle de l'hôte se trouvent au-dessus de chaque site trouvé sur disque :
Dynamic Protection, la vue en direct de ce qui frappe le serveur en ce moment :
Les bots connus, par catégorie (scanner / moteur de recherche / crawler IA), issus de
ArcJet's Well-Known Bots,
ai.robots.txt et de la
liste NGINX Ultimate Bad Bot Blocker.
Bloquer une catégorie injecte une règle if ($http_user_agent ...) dans la configuration NGINX
de chaque site (apply-blocks / a/A de Site settings).
Trop de requêtes, via la limitation de débit propre à NGINX. Contrairement à tout le reste ici, cela est appliqué par NGINX au moment de la requête plutôt qu'en analysant un journal après coup. Désactivé par défaut : une limite réglée pour le mauvais site éloigne de vrais visiteurs.
Poli, d'abord — un robots.txt généré en option listant chaque bot que vous bloquez,
pour les crawlers qui le respectent, plus le chemin honeypot ci-dessous. Désactivé par défaut, car il
remplace ce que votre site sert actuellement à /robots.txt.
Sauf là où vous en décidez autrement — des exemptions de chemins par site, pour que vous puissiez bloquer les crawlers IA
partout sauf /blog.
Les requêtes qui ne ressemblent pas à un navigateur, par site. Six règles indépendantes, chacune son propre interrupteur et chacune désactivée par défaut — un interrupteur par règle pour que si quelque chose chez vous cesse de fonctionner, vous puissiez identifier quelle règle en est la cause :
Deux garde-fous s'appliquent à toutes, et sont appliqués plutôt que laissés à votre charge :
server HTTPS. Les navigateurs
ne font pas de HTTP/2 sans TLS, donc sur un bloc listen 80 simple, chaque requête est en HTTP/1.1 —
y compris la redirection qu'un navigateur effectue en route vers HTTPS. Vos blocs port-80 et port-443
partagent généralement un server_name, donc le paramètre atteint les deux ; seul celui avec TLS reçoit
ces règles. Les règles de forme d'en-tête fonctionnent en HTTP simple et sont écrites dans les deux./.well-known/ est toujours exempté dès qu'une règle est activée. C'est là que Let's Encrypt
récupère son challenge HTTP-01, en HTTP/1.1 sans Accept et souvent sans User-Agent —
sans l'exemption, votre certificat cesse de se renouveler des semaines plus tard.Un choix à l'échelle de l'hôte, dans Site settings. Ce ne sont pas des codes de statut interchangeables — chacun dit quelque chose de différent, et la différence compte surtout pour les clients que vous n'aviez pas l'intention de prendre :
| Option | À quoi ça sert |
|---|---|
403 Forbidden (par défaut) | indique que le blocage était délibéré ; le seul sur lequel un humain pris par erreur peut agir |
404 Not Found | dissimule qu'il y a eu un blocage |
410 Gone | demande aux crawlers bien élevés d'abandonner l'URL définitivement — préférez ceci au 403 quand vous écartez des crawlers plutôt que des attaquants |
429 Too Many Requests | dit à un client poli de ralentir et de réessayer |
418 I'm a teapot | la blague de la RFC 2324. Ça fonctionne ; ce n'est simplement pas enregistré auprès de l'IANA, et NGINX l'envoie avec un corps vide |
444 close connection | aucune réponse du tout ; le moins coûteux, mais indiscernable d'un serveur en panne |
Tarpit | répond 403 mais fait couler le corps à un octet par seconde, pour que le client attende au lieu de passer à autre chose |
Le tarpit est l'option la plus douce pour un faux positif — un client pris par erreur est ralenti,
pas refusé — et la plus coûteuse pour un bot, dont la connexion reste inactive. Deux choses à
savoir avant de le choisir : il retient aussi une de vos connexions worker pendant toute la durée, donc
un flot de clients tarpités entre en concurrence avec les vrais visiteurs pour worker_connections ; et combien
de temps cela dure réellement dépend de la façon dont NGINX choisit d'écrire un petit corps d'erreur, ce qui est
noté dans TODO.md comme nécessitant une vérification contre un vrai serveur.
Chacun de ces éléments est un interrupteur indépendant sur le panneau « Automatic blocking » du Dashboard, et chacun ajoute un blocage de pare-feu temporaire qui expire de lui-même et est réajouté si le comportement persiste.
Ils s'exécutent sur un minuteur interne qui relit vos journaux d'accès SSH et NGINX toutes les minutes — mais uniquement tant que la TUI ou l'interface web est en cours d'exécution. L'une ou l'autre maintient le même calendrier, dans la même base de données, donc laisser l'interface web ouverte suffit ; rien n'est détecté quand aucune des deux n'est en cours d'exécution. Pour un serveur sans aucun processus stop-bots dessus, voir Unattended, from cron ci-dessous.
/.env, /.git/config,
/wp-config.php et similaires est concluante à elle seule, donc cela ne nécessite aucun seuil. La
liste intégrée omet délibérément les chemins qui sont légitimes quelque part — /wp-login.php,
/wp-admin/, /xmlrpc.php, /phpmyadmin — car verrouiller votre propre administrateur serait
pire que manquer un scanner que le détecteur de 404 attrape de toute façon. Ajoutez les vôtres avec
set-probe-paths.Disallow: dans le robots.txt généré et lié
nulle part. L'atteindre signifie ignorer robots.txt, ce qu'aucune entité légitime ne fait par accident —
le signal le plus fort ici, et le blocage le plus long. Nécessite que la génération de robots.txt soit activée pour
fonctionner du tout.Trois autres examinent comment un client se comporte plutôt que ce qu'il demande. Les trois sont désactivés par défaut, car chacun a un faux positif qu'il ne peut pas écarter seul — et tous les trois exemptent les crawlers de moteurs de recherche vérifiés, qui autrement correspondraient à chacun d'eux :
304 compte comme un asset récupéré).
Ne peut pas vous aider sur un site qui ne sert aucun asset du tout — une pure API JSON.Referer. Affaibli par
Referrer-Policy: no-referrer et les outils de confidentialité ; le seuil de chemins distincts est ce qui
le rend utilisable tout court./24 sont signalées
lors de la même passe, bloquez le /24. Désactivé par défaut — bloquer 256 adresses parce que trois
se sont mal comportées est un dommage collatéral assumé. (IPv6 est différent et ne nécessite aucun interrupteur : une détection
bloque toujours le /64, car un /64 est un LAN, la même chose qu'une seule adresse IPv4
représente. Bloquer la seule adresse qu'un attaquant IPv6 a utilisée n'arrêterait rien
— il en a 2^64 autres.)Tout ce qui précède est généré. Savoir si tout ou partie est en vigueur est une question
distincte, et stop-bots status est ce qui y répond :```
stop-bots status
Sept vérifications, et la première est celle qui vaut la peine : les règles générées sont-elles réellement dans le noyau, ou seulement sur le disque ? Un hôte réel a tourné pendant trois semaines avec 48 860 règles de drop dans `/etc/stop-bots/firewall.nft` et un ensemble de règles vide, parce qu'écrire le script et le charger sont deux étapes et que personne n'avait jamais regardé la seconde.
Le reste : l'ensemble de règles survivra-t-il à un redémarrage (`nftables.service` activé ?), le script correspond-il toujours aux règles, les blocs NGINX sont-ils appliqués, le service de console exécute-t-il le binaire qu'il nomme, y a-t-il de la place pour la base de données, et les détecteurs peuvent-ils lire leurs journaux.
Il se termine avec un code non nul si quelque chose est **CRITICAL**, ce qui en fait une vérification de supervision. `--quiet` n'affiche que ce qui nécessite attention, ce qui est la forme adaptée à cron :```
0 * * * * /usr/local/bin/stop-bots status --quiet
Un contrôle qui n'a pas pu s'exécuter — nft list nécessite root — rapporte UNKNOWN, jamais
OK. Un contrôle de santé qui dit que tout va bien parce qu'il n'a pas pu regarder est
pire que rien, parce qu'on y croit.
Le même rapport figure sur le Dashboard dans la console et dans la TUI, relevé
toutes les heures par le cron interne plutôt qu'à chaque rendu : nft list sur un grand
ensemble de règles représente des mégaoctets de texte.
Chaque décision de pare-feu ci-dessus est générée, jamais appliquée automatiquement : render-firewall
(ou la touche f du Dashboard) écrit un script iptables ou nftables que vous pouvez examiner et appliquer
vous-même, et refuse d'en écrire un qui verrouillerait une session SSH actuellement connectée.
Trois choses peuvent l'appliquer pour vous, et toutes les trois nécessitent que vous le demandiez : la popup de rendu de la TUI
(« apply after writing ») ou sa touche a, le panneau pare-feu de la console web (« run it after
writing ») ou son bouton « Apply everything », et batch --apply depuis un crontab que vous avez écrit — voir
Unattended, from cron. Aucune d'entre elles n'est un effet de bord de quoi que ce soit
d'automatique : le cron interne génère le script et ne l'exécute jamais.
Il en va de même côté NGINX : modifier un paramètre ne change que ce qui serait écrit.
Les paramètres de site affichent chaque site comme STALE jusqu'à ce que vous appliquiez.
Désactiver un détecteur ne supprime jamais les blocages qu'il a déjà ajoutés — ceux-ci expirent d'eux-mêmes.
« Arrêter de détecter » et « annuler ce qui a été détecté » sont délibérément distincts ; le second est
l'écran Dynamic Protection ou remove-firewall-rule.
Il existe aussi un simple décompte des journaux d'accès, indépendant du blocage : record-access-stats /
list-access-stats comptent la fréquence d'apparition de chaque user agent dans les requêtes réussies (non
erronées), afin que vous puissiez voir qui visite réellement en plus de qui est bloqué.
stop-bots batch est une passe sur tout ce que la TUI fait à la main : rafraîchir chaque liste,
analyser les journaux, écrire les règles de blocage NGINX et le script de pare-feu.```
0 4 * * * root /usr/local/bin/stop-bots batch --apply --ssh-log /var/log/auth.log
*/10 * * * * root /usr/local/bin/stop-bots batch --apply --no-fetch --ssh-log /var/log/auth.log
Il ne dit rien quand tout a fonctionné, donc une exécution nocturne saine ne vous envoie pas de mail. Une étape échouée écrit sur stderr et définit un statut de sortie non nul, ce qui fait que cron vous en informe. Lancez-le une fois à la main avec `--verbose` d'abord — cela affiche une ligne par étape, et c'est le moyen le plus simple de voir ce qu'il fait réellement.
**`--apply` est ce qui lui fait appliquer quoi que ce soit.** Sans lui, `batch` écrit la configuration NGINX et le script de pare-feu puis s'arrête : la configuration ne fait rien tant qu'il n'y a pas de rechargement, un script ne fait rien tant qu'il n'est pas exécuté. C'est la valeur par défaut de ce projet partout, et elle le reste ici.
`batch` et un front-end de longue durée coexistent sans problème. La TUI, l'interface web et `batch` enregistrent tous ce qu'ils ont fait via les mêmes clés dans la même base de données, donc celui qui atteint une tâche en premier l'exécute et les autres constatent qu'elle n'est plus due — vous n'obtenez pas deux passes de détection, et le panneau « Scheduled tasks » du Dashboard montre ce qui s'est réellement passé plutôt que de prétendre que tout est en retard. Si vous laissez déjà l'interface web tourner, l'entrée nocturne `batch` est une ceinture et des bretelles plutôt qu'une nécessité ; si ce n'est pas le cas, c'est la seule chose qui maintient la détection à jour.
**Avec `--apply`, le garde-fou contre le verrouillage SSH peut refuser — et refuser signifie que rien n'est appliqué.** Il refuse si les règles bloqueraient un client actuellement connecté, *et* si aucun journal SSH n'a pu être lu du tout, car alors la vérification n'a pas pu s'exécuter. Le `render-firewall` interactif affiche seulement une note dans ce second cas, en partant du principe qu'un humain surveille le terminal ; depuis cron, personne ne le fait. **Passez `--ssh-log` explicitement** : cron s'exécute en root donc `/var/log/auth.log` se lit généralement bien, mais sur un hôte journald uniquement, `journalctl` sous cron peut revenir vide, ce qui est exactement le cas pour lequel il refuse. `--force` outrepasse le garde-fou si vous le voulez vraiment.
L'échec d'une étape n'arrête jamais les autres, et les moitiés NGINX et pare-feu sont indépendantes — un rechargement NGINX échoué laisse quand même le pare-feu appliqué, et inversement.
`batch` enregistre chaque étape selon le même planning que le cron interne de la TUI utilise, donc les deux s'accordent sur ce qui a déjà été exécuté au lieu de le faire tous les deux, et le panneau « Scheduled tasks » du Dashboard montre ce que votre vrai cron a fait.
# L'interface web
`stop-bots web` sert les mêmes cinq écrans dans un navigateur.```
stop-bots web
Il se lie à 127.0.0.1:8787 — accessible uniquement depuis cette machine — et affiche un mot de passe généré une seule fois, au premier lancement. Accédez-y depuis votre ordinateur portable via un tunnel SSH :``` ssh -L 8787:127.0.0.1:8787 your-server
puis ouvrez <http://127.0.0.1:8787/>.

La console suit le réglage clair ou sombre du système d'exploitation, avec un bouton bascule dans
l'en-tête ; les captures d'écran du TUI ci-dessus correspondent au thème sombre, celles-ci au thème clair. Les touches que
le TUI utilise fonctionnent ici aussi : `1`–`4` changent d'écran, `/` place le focus sur le champ de recherche, `?` ouvre l'aide.

Trois actions à l'échelle de l'hôte se trouvent dans l'en-tête, sous forme de boutons dans le navigateur et de touches
uniques dans le TUI :
- **Tout mettre à jour** (`u`) télécharge chaque liste de bots, chaque plage d'IP de crawlers, chaque
flux de réputation *activé* et chaque pays *sélectionné* — le même ensemble que `stop-bots batch`
récupère, à partir du même plan. L'échec d'une source n'arrête pas les autres, et rien n'est
appliqué tant que quelque chose ne le fait pas.
- **Tout appliquer** (`a`) écrit et recharge la configuration NGINX, puis écrit et exécute le
script de pare-feu. Les deux plans sont indépendants : quel que soit celui qui échoue, l'autre passe quand même
son tour, car un hôte à moitié appliqué vaut mieux qu'un hôte où une erreur de syntaxe NGINX a aussi laissé
le pare-feu obsolète.
- **Web Access** (`w`) configure NGINX pour servir la console elle-même — voir
[Derrière NGINX](#behind-nginx-a-subdomain-or-a-path-prefix).
## En tant que service (Debian)```
sudo stop-bots install web
Écrit /etc/systemd/system/stop-bots-web.service, crée /var/lib/stop-bots (0700 — il
contient le hachage du mot de passe de la console) et /etc/stop-bots, génère un mot de passe s'il n'y en a pas,
puis active et démarre l'unité.
--dry-run affiche l'intégralité du plan et ne modifie rien. C'est la seule commande du
projet qui démarre un démon, alors commencez par là. --prefix <dir> écrit la même arborescence quelque part
où vous pouvez la lire sans être root. Si l'unité existe déjà et que vous l'avez modifiée, l'
installateur s'arrête et le signale plutôt que de remplacer votre modification ; --force si c'est bien ce que vous voulez.
Le service s'exécute en tant que root, car la console réécrit /etc/nginx, écrit le script
du pare-feu, et exécute nginx -t et systemctl reload nginx. Il n'existe pas de découpage non privilégié qui
laisse l'ensemble des fonctionnalités intact. L'unité porte le durcissement qui survit à cette exigence
et un commentaire indiquant quel durcissement a été omis et pourquoi.
L'adresse d'écoute, la liste d'autorisation des hôtes et le préfixe de chemin ne sont délibérément pas dans l'unité — le
serveur en cours d'exécution les relit depuis la base de données, donc les mettre dans ExecStart leur donnerait
deux sources de vérité. Modifiez-les avec stop-bots web --save ... et redémarrez.
Une chose change une fois que cela s'exécute en tant que root : la tâche quotidienne RenderFirewall du cron
interne peut désormais écrire /etc/stop-bots/firewall.nft, ce qu'elle ne pouvait pas faire lorsque vous exécutiez la console à la main
en tant que vous-même. Rien n'applique ce script — l'exécuter reste à votre charge.
Seul Debian est vérifié, car c'est ce qui a été testé ; l'unité est très probablement correcte sur toute distribution systemd, mais le chemin de journal SSH qu'elle suppose est celui de Debian.
Lier autre chose que loopback nécessite un second drapeau délibéré, car cette console peut réécrire le pare-feu et la configuration NGINX de l'hôte sur lequel elle s'exécute :``` stop-bots web --bind 0.0.0.0:8787 --expose --allowed-hosts admin.example.com --save
`--allowed-hosts` n'est pas optionnel en pratique : une requête portant un nom d'hôte qui n'est pas
listé est refusée. C'est ce qui fait échouer le DNS rebinding contre la console, et c'est
pourquoi un serveur exposé atteint par nom a besoin que le nom soit explicitement indiqué.
Placez-le derrière NGINX avec TLS — le même NGINX que cet outil protège. Si vous le faites, et que le
proxy définit `X-Forwarded-For`, indiquez à la console qu'elle peut croire cet en-tête, sinon elle ne peut pas
déterminer de quelle adresse une requête provient réellement :```
stop-bots web --bind 127.0.0.1:8787 # and set web:trust_forwarded_for
Derrière TLS, définissez aussi web:secure_cookie. Sans cela, un navigateur enverra également le cookie de session à une URL http:// pour le même hôte.
web:trust_forwarded_for compte plus qu'il n'y paraît. Sans lui, chaque requête derrière un proxy arrive depuis 127.0.0.1, donc la console ne peut pas distinguer un client d'un autre — ce qui signifie qu'un flot de tentatives de connexion partage le même compartiment de limitation que vous, et le garde-fou qui vous empêche de bloquer votre propre adresse n'a rien avec quoi comparer. Avec lui, les deux fonctionnent par client.
La console peut configurer cela pour vous, et le TUI aussi (w sur le Dashboard). Les deux écrivent la configuration NGINX, enregistrent le préfixe de chemin et ajoutent le nom d'hôte à la liste d'autorisation — les trois éléments qui doivent concorder, car un préfixe manquant fait sortir chaque lien du bloc location et un nom d'hôte manquant transforme chaque requête en 403. Les deux valident avec nginx -t avant que la configuration puisse prendre effet, la restaurent en cas d'échec, et n'enregistrent la nouvelle adresse qu'une fois validée.
Deux modes, et path est celui par défaut pour une raison : il ajoute un bloc location à un site que vous avez déjà, donc la console hérite du certificat de ce site. Un sous-domaine a besoin du sien, et tant que certbot --nginx -d <host> n'a pas été exécuté, le formulaire de mot de passe et le cookie de session de cette console traversent le réseau en clair.
Le reste de cette section est la même chose à la main, ce qui vaut la peine d'être lu une fois même si vous utilisez le panneau — le piège de la barre oblique finale ci-dessous est l'erreur qu'il existe pour prévenir.
Un sous-domaine est le déploiement le plus simple, et celui à choisir si vous le pouvez :```nginx server { server_name stopbots.example.com; location / { proxy_pass http://127.0.0.1:8787; proxy_set_header Host $host; } }
| `-s` | `--server` | `SERVER` | `http://localhost:8080` | URL du serveur cible |
| `-t` | `--token` | `TOKEN` | - | Jeton d'authentification |
| `-o` | `--output` | `FILE` | - | Chemin du fichier de sortie |
| `-v` | `--verbose` | - | `false` | Activer la sortie détaillée |
| `-q` | `--quiet` | - | `false` | Supprimer la sortie non essentielle |
| `--timeout` | - | `SECONDS` | `30` | Délai d'expiration de la requête en secondes |
| `--retry` | - | `COUNT` | `3` | Nombre de tentatives en cas d'échec |
| `--insecure` | - | - | `false` | Ignorer la vérification du certificat TLS |
| `--proxy` | - | `URL` | - | URL du proxy à utiliser |
| `--config` | - | `FILE` | `~/.tool/config.yaml` | Chemin du fichier de configuration |
| `--no-color` | - | - | `false` | Désactiver la sortie colorée |
| `--debug` | - | - | `false` | Activer la journalisation de débogage |
### Exemples
```bash
# Exécution de base
tool scan --target example.com
# Avec jeton d'authentification
tool scan --target example.com --token "your-api-token"
# Sortie vers un fichier
tool scan --target example.com --output results.json
# Mode détaillé avec délai d'expiration personnalisé
tool scan --target example.com --verbose --timeout 60
# Utilisation d'un proxy
tool scan --target example.com --proxy "http://proxy.example.com:8080"
# Ignorer la vérification TLS (non recommandé en production)
tool scan --target example.com --insecure
L'outil prend en charge un fichier de configuration YAML pour définir les options par défaut :
# ~/.tool/config.yaml
server: "http://localhost:8080"
token: "your-api-token"
timeout: 30
retry: 3
verbose: false
output: "results.json"
L'outil génère une sortie structurée dans plusieurs formats :
{
"target": "example.com",
"timestamp": "2024-01-15T10:30:00Z",
"status": "completed",
"findings": [
{
"id": "FINDING-001",
"severity": "high",
"title": "Exemple de vulnérabilité",
"description": "Description détaillée de la vulnérabilité",
"remediation": "Étapes pour corriger le problème"
}
]
}
[+] Cible : example.com
[+] Statut : Terminé
[+] Constats : 1
[HIGH] FINDING-001 : Exemple de vulnérabilité
Description : Description détaillée de la vulnérabilité
Remédiation : Étapes pour corriger le problème
id,severity,title,description
FINDING-001,high,Exemple de vulnérabilité,Description détaillée de la vulnérabilité
| Code | Signification |
|---|---|
0 | Succès |
1 | Erreur générale |
2 | Arguments de ligne de commande invalides |
3 | Échec de la connexion au serveur |
4 | Échec d'authentification |
5 | Délai d'expiration de la requête dépassé |
6 | Permission refusée |
7 | Fichier introuvable |
8 | Format de sortie non pris en charge |
Connexion refusée
Échec d'authentification
Délai d'expiration de la requête dépassé
--timeoutPermission refusée
Activez la journalisation de débogage pour obtenir des informations détaillées :
tool scan --target example.com --debug
Cela affichera des informations détaillées sur les requêtes, les réponses et les erreurs.``` stop-bots web --allowed-hosts stopbots.example.com --save
**Un préfixe de chemin fonctionne aussi**, mais la console doit en être informée — elle doit
générer chaque lien, action de formulaire, redirection et chemin de cookie avec le préfixe déjà
inclus, et elle ne peut pas le deviner :```
stop-bots web --base-path /stop-bots --allowed-hosts example.com --save
| -s | --server | string | URL du serveur C2 (par défaut : http://127.0.0.1:8080) |
| -p | --password | string | Mot de passe pour l'authentification du client |
| -k | --key | string | Clé de chiffrement pour la communication |
| -i | --interval | int | Intervalle de beacon en secondes (par défaut : 5) |
| -j | --jitter | float | Pourcentage de gigue pour l'intervalle de beacon (par défaut : 0.2) |
| -t | --timeout | int | Délai d'expiration de la requête en secondes (par défaut : 30) |
| -r | --retries | int | Nombre de tentatives de reconnexion (par défaut : 3) |
| -d | --debug | bool | Activer la journalisation de débogage |
| -v | --version | bool | Afficher la version et quitter |
| -h | --help | bool | Afficher le message d'aide et quitter |
# Démarrer le client avec les paramètres par défaut
./client
# Se connecter à un serveur C2 spécifique avec un mot de passe
./client -s https://c2.example.com:8443 -p mypassword
# Définir un intervalle de beacon de 10 secondes avec 30% de gigue
./client -i 10 -j 0.3
# Activer la journalisation de débogage
./client -d
Le client peut être configuré à l'aide d'un fichier de configuration ou de variables d'environnement. Le fichier de configuration est recherché dans les emplacements suivants :
./config.yaml$HOME/.config/client/config.yaml/etc/client/config.yaml| Variable | Description |
|---|---|
CLIENT_SERVER | URL du serveur C2 |
CLIENT_PASSWORD | Mot de passe pour l'authentification du client |
CLIENT_KEY | Clé de chiffrement pour la communication |
CLIENT_INTERVAL | Intervalle de beacon en secondes |
CLIENT_JITTER | Pourcentage de gigue pour l'intervalle de beacon |
CLIENT_TIMEOUT | Délai d'expiration de la requête en secondes |
CLIENT_RETRIES | Nombre de tentatives de reconnexion |
CLIENT_DEBUG | Activer la journalisation de débogage |
server: https://c2.example.com:8443
password: mypassword
key: myencryptionkey
interval: 10
jitter: 0.3
timeout: 30
retries: 3
debug: false
# Cloner le dépôt
git clone https://github.com/example/client.git
cd client
# Compiler le client
make build
# Ou compiler directement avec Go
go build -o client ./cmd/client
# Compiler pour plusieurs plateformes
make build-all
# Compiler pour Linux (amd64)
GOOS=linux GOARCH=amd64 go build -o client-linux-amd64 ./cmd/client
# Compiler pour Windows (amd64)
GOOS=windows GOARCH=amd64 go build -o client-windows-amd64.exe ./cmd/client
# Compiler pour macOS (amd64)
GOOS=darwin GOARCH=amd64 go build -o client-darwin-amd64 ./cmd/client
# Exécuter tous les tests
make test
# Exécuter les tests avec couverture
make test-coverage
# Exécuter les tests d'intégration
make test-integration
Cet outil est fourni à des fins éducatives et de recherche en sécurité uniquement. Les auteurs ne sont pas responsables de toute utilisation abusive ou de tout dommage causé par ce logiciel. Utilisez-le de manière responsable et conformément aux lois applicables.
Ce projet est sous licence MIT - voir le fichier LICENSE pour plus de détails.
**Le slash final dans `proxy_pass` est important, et son absence est toute l'astuce.**
Sans lui, NGINX transmet le chemin complet et `stop-bots` voit
`/stop-bots/whatever`, ce qu'il sert et génère désormais. *Avec* un slash
final, NGINX supprime le préfixe — et le navigateur résout alors les liens de la page
par rapport à la racine du domaine, atterrit en dehors du bloc `location`, et tout renvoie une 404. Aucun
soin côté serveur ne peut corriger cela, donc le préfixe doit survivre au
proxy.
Rien ne l'impose de l'extérieur, mais l'échec est bruyant plutôt que subtil : avec le
préfixe configuré, une requête sans préfixe est une simple 404 plutôt qu'une page qui
fonctionne à moitié.
## Ce qu'il ne fera pas
Deux choses manquent volontairement, et l'écran d'aide le dit avec les raisons :
- **Il ne débloquera pas quelque chose qu'une liste téléchargée a bloqué** — le prochain rafraîchissement de cette
liste l'annulerait silencieusement.
- **Il ne changera pas son propre mot de passe.** Utilisez `stop-bots web --set-password` sur l'hôte.
Il refuse également de bloquer l'adresse depuis laquelle vous êtes connecté, ce qui supprimerait la
console que vous utiliseriez pour l'annuler.
**Il y en avait trois auparavant.** Appliquer le script de pare-feu était la troisième, au motif que
l'exécuter est la seule opération qui peut retirer l'hôte du réseau. C'est désormais
disponible — « Apply everything » sur le Dashboard (`a` dans la TUI), ou la case « run it after
writing » dans le panneau du pare-feu — parce que la garde qui le rend sûr depuis cron le rend sûr depuis un
bouton : les règles sont vérifiées par rapport aux clients actuellement connectés en SSH, dans
l'ordre où le script lui-même les évaluera, et une règle qui en bloquerait un est un
refus plutôt qu'un avertissement. Lancez la console avec `--no-apply` pour retrouver l'ancien
comportement en écriture seule.
Les tentatives de connexion sont limitées. Non pas parce que le mot de passe est devinable — il est généré,
144 bits — mais parce que vérifier un mot de passe exécute Argon2id, et laisser un appelant non authentifié
le solliciter aussi vite qu'il peut envoyer des requêtes est un déni de service contre l'hôte que cet outil
est censé protéger. Dix tentatives erronées sont gratuites ; au-delà, un client ralentit
exponentiellement, et un plafond global borne le CPU quel que soit le nombre d'adresses d'où
proviennent les tentatives.
## Exécuter NGINX dans un conteneur
Si NGINX est dans Docker et que sa configuration est sur un bind mount, `systemctl reload nginx` ne recharge
rien. Pointez les deux commandes vers le conteneur à la place — cela s'applique aussi au CLI et à la
TUI :```
stop-bots set-nginx-commands \
--test "docker exec web nginx -t" \
--reload "docker exec web nginx -s reload"
La commande est divisée en mots et exécutée directement. Elle ne passe jamais par un shell, donc ;,
| et $VAR sont des caractères ordinaires plutôt que de la syntaxe.
L'organisation du code, la façon dont il est testé et les règles selon lesquelles il est écrit se trouvent dans CONTRIBUTING.md. Le processus de publication est dans RELEASING.md.
Vous pouvez me contacter à [email protected].
Copyright (C) 2026 Marko Ivankovic
Ce programme est un logiciel libre : vous pouvez le redistribuer et/ou le modifier selon les termes de la GNU Affero General Public License telle que publiée par la Free Software Foundation, soit la version 3 de la Licence, soit (à votre choix) toute version ultérieure.
Consultez le fichier LICENSE pour le texte complet de la Licence.
Une licence alternative n'est PAS disponible.
| Règle | Écarte, outre les bots |
|---|
| HTTP/1.0 et HTTP/1.1 | les crawlers et clients API qui ne parlent pas HTTP/2 |
Pas d'en-tête Accept | certains clients API n'en envoient aucun |
Pas d'Accept-Language | les outils de confidentialité le suppriment |
User-Agent vide/absent | les scripts et vérifications de santé l'omettent souvent |
Host est une IP nue | empêche d'atteindre le site par IP |
| TLS 1.0 / 1.1 | uniquement les clients très anciens |