Les bots malveillants ne s’arrêtent pas. Coupez-les avant l’application – Nettoyage des URL suspectes à l’étape nginx

Nettoyer les URL suspectes avec le fichier blackhole.conf de nginx

Dès que vous exposez une application web à Internet, quel que soit le framework, vous recevez une avalanche de requêtes suspectes.

  • /wp-admin/ qui n’existe pas
  • /index.php, /xmlrpc.php alors qu’aucune ligne PHP n’est écrite
  • .git, .env, /cgi-bin/ – fichiers ou répertoires sensibles
  • wso.php, shell.php, test.php

Ce ne sont pas vos propres services qui font ces requêtes, mais les scanners et bots du monde entier qui testent la présence de vulnérabilités. En toute honnêteté, il n’existe pas de serveur qui ne subisse pas ces attaques.

Heureusement, si votre niveau applicatif est bien protégé, la plupart de ces requêtes sont déjà renvoyées avec un 400/404. Le problème survient ensuite.

  • Le journal se pollue. Les requêtes étranges remplissent la moitié de la page de logs avant même d’atteindre les vrais utilisateurs.
  • Un peu de CPU est consommé. Même si vous pensez que c’est « sûr », il est dérangeant de savoir que des calculs inutiles sont effectués.
  • La fatigue mentale s’accumule. Chaque fois que vous consultez les logs, des centaines de lignes /wp-login.php s’affichent…

C’est pourquoi je préfère coupper ces requêtes dès le niveau nginx, avant qu’elles n’atteignent l’application. En d’autres termes, je les envoie dans un « blackhole » au niveau hôte.

Dans cet article, je vais expliquer :

  • Pourquoi un blocage précoce à l’étape nginx est utile
  • Comment gérer les règles communes avec un fichier blackhole.conf
  • Un exemple de configuration nginx réellement exploitable

1. Pourquoi bloquer uniquement au niveau de l’application est insuffisant



La démarche habituelle consiste à :

  • Renvoi 404 pour les URL non définies dans le routeur/contrôleur
  • Gestion d’exception → 400/500
  • Journalisation → collecte via APM ou collecteur de logs

Fonctionnellement, cela fonctionne. Mais d’un point de vue opérationnel, plusieurs points sont problématiques.

  1. Bruit de journal * Le ratio erreurs/404 est gonflé par les scanners, pas par les vrais utilisateurs. * Il faut constamment trier visuellement.
  2. Blocage trop bas * Si la requête atteint l’application, elle a déjà traversé le framework, le middleware et la couche de routage. * On se demande alors : « Pourquoi laisser passer cette requête ? »
  3. Consommation cumulée * Un ou deux trafics ne posent pas de problème, mais un scanner qui envoie 24h/24 peut consommer un nombre important de requêtes.

Je préfère donc nettoyer ces requêtes dès le niveau supérieur.


2. Créer un blackhole dans nginx : couper immédiatement avec 444

Nginx possède un code d’état autre que les standards HTTP :

return 444;
  • Aucun en-tête ni corps n’est renvoyé
  • La connexion TCP est simplement fermée silencieusement
  • Du point de vue du client, on ne voit qu’une rupture de connexion

En utilisant ce code, vous pouvez ne pas répondre du tout aux URL que vous jugez 100 % suspectes.

Avantages :

  • L’application n’est jamais atteinte (CPU de framework = 0)
  • Vous pouvez désactiver complètement les logs d’accès pour ces requêtes
  • Les logs de l’application restent propres

3. Gérer les règles avec un seul fichier blackhole.conf



Au lieu d’écrire les règles dans chaque bloc serveur, je garde un fichier blackhole.conf qui regroupe les motifs communs.

# /etc/nginx/blackhole.conf (chemin libre)

# === 1. Répertoires de configuration cachés (.git, .env, IDE, etc.) ===
location ~* (^|/)\.(git|hg|svn|bzr|DS_Store|idea|vscode)(/|$)   { return 444; }
location ~* (^|/)\.env(\.|$)                                    { return 444; }
location ~* (^|/)\.(?:bash_history|ssh|aws|npm|yarn)(/|$)       { return 444; }

# === 2. CMS administrateurs non utilisés / scans de vulnérabilités ===
location ^~ /administrator/                                    { return 444; }  # Joomla, etc.
location ^~ /phpmyadmin/                                      { return 444; }

# === 3. Traces PHP/CGI/WordPress ===
# Recommandé uniquement pour les stacks qui n’utilisent pas PHP
location ~* \.(?:php\d*|phtml|phps|phar)(/|$)                  { return 444; }
location ^~ /cgi-bin/                                         { return 444; }
location ~* ^/(wp-admin|wp-includes|wp-content)(/|$)          { return 444; }

# === 4. Fichiers/chemins fréquemment scannés ===
location ~* ^/(?:info|phpinfo|test|wso|shell|gecko|moon|root|manager|system_log)\.php(?:/|$)? {
    return 444;
}
location ~* ^/(?:autoload_classmap|composer\.(?:json|lock)|package\.json|yarn\.lock|vendor)(?:/|$) {
    return 444;
}
location ~* ^/(?:_profiler|xmrlpc|xmlrpc|phpversion)\.php(?:/|$)? {
    return 444;
}

# === 5. Sauvegardes / temporaires / dumps ===
location ~* \.(?:bak|backup|old|orig|save|swp|swo|tmp|sql(?:\.gz)?|tgz|zip|rar)$ {
    return 444;
}

# === 6. well-known : autoriser uniquement ACME, couper le reste ===
location ^~ /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
}
location ^~ /.well-known/ {
    return 444;
}

# === 7. Méthodes restreintes (optionnel) ===
# Si vous n’utilisez pas TRACE, CONNECT, WebDAV, etc.
if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS)$) {
    return 405;
}

Ensuite, dans chaque bloc serveur, il suffit d’inclure ce fichier :

http {
    # ...

    server {
        listen 80;
        server_name example.com;

        include /etc/nginx/blackhole.conf;

        # Reste de la configuration routage normal...
    }
}

Ainsi, la plupart des scans de vulnérabilités sur des chemins inutilisés sont éliminés avant d’atteindre l’application.


4. Réduire encore le bruit des logs (optionnel)

Si vous souhaitez que les requêtes envoyées dans le blackhole n’apparaissent même pas dans access_log, vous pouvez utiliser une map.

http {
    map $request_uri $drop_noise_log {
        default                            0;
        ~*^/(?:\.git|\.env)                1;
        ~*^/(wp-admin|wp-includes|cgi-bin) 1;
        ~*\.php(?:/|$)                     1;
    }

    server {
        listen 80;
        server_name example.com;

        include /etc/nginx/blackhole.conf;

        access_log /var/log/nginx/access.log combined if=$drop_noise_log;
    }
}
  • $drop_noise_log vaut 1 pour les requêtes à ignorer
  • Seuls les vrais utilisateurs et les routes valides sont loggés

En production, il est conseillé de démarrer avec un log complet, valider les motifs, puis passer à if=$drop_noise_log une fois sûr qu’aucun utilisateur légitime n’est affecté.


5. Guide pour éviter les faux positifs

Les règles de blocage doivent rester pas trop agressives. Voici quelques critères.

  1. Adaptez les règles à votre stack * Si vous n’utilisez pas du tout PHP, bloquer .php est acceptable. * Si vous utilisez Laravel ou WordPress, retirez la règle .php. * Si vous avez un système hérité qui utilise /cgi-bin, excluez cette règle.
  2. Commencez par 404/403 * Vous pouvez d’abord renvoyer 403 pour tester, puis passer à 444 après quelques jours de surveillance.
  3. Traitez les chemins critiques avec prudence * .git, .env, fichiers de sauvegarde, phpinfo.php sont des cibles sûres à bloquer.
  4. Comprenez la priorité des location * Placez include blackhole.conf en haut du bloc serveur, avant votre location / { ... } principal.

6. Application, WAF, nginx : chacun fait son travail

Cette approche est surtout un nettoyage de bruit initial.

  • Nginx blackhole : coupe les chemins qui ne devraient jamais exister.
  • Validation au niveau applicatif : logique métier, autorisations, validation d’entrée.
  • WAF / solutions de sécurité : signatures, DDoS L7, filtrage avancé des bots.

Chaque couche ne remplace pas l’autre, mais les complète. Le blackhole de nginx est simple à mettre en place, très efficace, et apporte un bénéfice immédiat en termes de santé mentale et de ressources serveur.


Conclusion

Pour tout service exposé sur Internet, il est pratiquement impossible d’éviter les scans d’URL malveillants. La meilleure stratégie est de ne pas les laisser atteindre l’application.

« Si vous savez qu’elles vont arriver, bloquez-les avant qu’elles n’atteignent votre application. »

En gérant les motifs communs dans un blackhole.conf et en renvoyant 444, vous obtenez :

  • Des logs d’application plus propres
  • Une surveillance et une analyse plus simples
  • Un peu de marge de manœuvre sur les ressources serveur

La prochaine fois que vous ouvrirez vos logs à 2 h du matin, vous verrez moins de /wp-login.php et plus de requêtes réelles. C’est un plaisir.

Si vous avez déjà un nginx en production, créez votre propre blackhole.conf et intégrez‑le. En quelques jours, vous vous demanderez pourquoi vous n’aviez pas fait cela plus tôt.

image of nginx block malicious url