6135 links
  • GuiGui's Show
  • Home
  • Login
  • RSS Feed
  • ATOM Feed
  • Tag cloud
  • Picture wall
  • Daily
Links per page: 20 50 100
 
  • Block bruteforce attack on WordPress wp-login.php with Nginx only

    Nginx prend en charge nativement le rate-limiting, c'est-à-dire la limitation du nombre de requêtes par intervalles de temps sur une page web, sur plusieurs pages web, sur un site web entier. Il est même possible de mettre des adresses IP en liste blanche, de limiter uniquement les requêtes de type POST, de cumuler plusieurs critères de limitation (par hôte virtuel + par adresse IP source, par exemple), etc.

    Dans mon cas, le CAPTCHA maison d'un formulaire de contact web est extrêmement faillible. C'est une leçon d'humilité pour les anti-Google dans mon genre : on ne remplace pas reCAPTCHA en un claquement de doigts. Comme dans tout formulaire, un email est envoyé à l'administrateur du site web et un autre à la personne qui a rempli le formulaire. En changeant la valeur du champ HTML « email », il est donc possible d'envoyer un email à n'importe quelle adresse email. Bien sûr, le sujet de l'email ne sera pas au choix du spammeur et le message du spammeur sera entouré de phrases convenues ajoutées par le site web comme « vous avez envoyé le message suivant à l'administrateur du site machin.example », mais cela convient aux spammeurs.



    En attendant que les développeurs nous informent qu'ils feront rien et que nos utilisateurs nous valident que ce formulaire est inutile car ils ne souhaitent pas traiter les réclamations par email mais par une demande dans notre système de tickets, une limitation du trafic a été utile pour juguler temporairement le spam sans perdre en fonctionnalité.

    D'abord, je déclare un limiteur dans /etc/nginx/conf.d/rate-limiting.conf (mieux vaut ici que de modifier nginx.conf, car ça permet au gestionnaire de paquets du système de mettre à jour nginx.conf sans intervention humaine) : limit_req_zone $binary_remote_addr zone=contact:10m rate=1r/m;. On travaille sur l'adresse IP (stockée, par nginx, dans la variable interne « $binary_remote_addr »). On nomme le limiteur « contact ». Il pourra occuper 10 Mo de mémoire tout au plus (ce qui permet de stocker environ 16 000 états IPv4 ou 8 k états IPv6). Il limitera le trafic à une requête par minute.

    Ensuite, j'active le limiteur dans le bloc « server » de l'hôte virtuel :

    location /contact {
        limit_req   zone=contact burst=3 nodelay;
        uwsgi_pass  django;
    }

    Je limite les requêtes uniquement sur la page « /contact » du site web.

    J'utilise le limiteur nommé « contact ». Au-delà d'une requête par minute, j'autorise un pic jusqu'à 3 requêtes (par minute, toujours). Je dégage au plus vite les requêtes excédentaires (au-delà du pic) afin de ne pas consommer inutilement des ressources (nombre de connexions simultanées, RAM, etc.) plutôt que de mettre en file d'attente les requêtes excédentaires en attendant qu'un slot se libère après l'expiration du temps d'attente (1 minute tout au plus).

    « django » fait référence à un upstream de type socket fichier défini en amont. Car, oui, ce nginx est en frontal d'une application Python Django servie par le serveur d'applications uwsgi.

    Je recharge la configuration avec systemctl restart nginx, et c'est terminé. \o/

    Avec cette configuration, on est passé de 3000 spams en quelques heures à deux spams par heure puis l'emmerdeur a laissé tomber. :)



    Si j'avais voulu limiter uniquement les requêtes POST, voici ce que j'aurai mis dans /etc/nginx/conf.d/rate-limiting.conf :

    map $request_method $limit_post {
        default    "";
        POST       $binary_remote_addr;
    }
    
    limit_req_zone $limit_post zone=contact:10m rate=1r/m;

    Ça ne fonctionnait pas car notre spammeur avait résolu le CAPTCHA. Dit autrement : il faisait plusieurs requêtes de type GET sur le formulaire web. Dès qu'il reconnaissait l'URL d'une image qu'il avait décodé (exemple : s'il trouvait <img src="/images/captcha_a52b844ab3acbd1169db9a66647285b5fc694284/" […] /> dans le code source de la page web, alors il savait qu'il fallait remplir le champ HTML « captcha » avec la valeur « 7 » car cette URL pointera toujours sur un CAPTCHA qui dit « 5 + 2 = ? », seul le fond, le bruit, changera), il envoyait une requête POST, et, forcément, il tapait dans le mille dès cette première requête, ce qui met en échec toute limitation de trafic.



    Au final, puisque le CAPTCHA est faillible, que l'application web envoie l'email destiné à l'administrateur à une adresse email invalide sans tenir compte de la configuration (autre bug signalé), que les développeurs ne corrigeront pas l'application web, et que nos utilisateurs ne veulent pas traiter les réclamations par email, nous avons mis en place une page web qui invite les visiteurs à déposer une demande d'assistance dans notre outil de gestion de ces demandes.

    Pour ce faire, voici la nouvelle configuration que l'on trouve dans l'hôte virtuel de nginx :

    location /contact {
        root /chemin/vers/ma/super/appli/;
        try_files /erreurContact.html $uri;
    }

    La racine du site web (document root) est tel chemin. Lorsqu'une requête arrive, essaye de servir le fichier « erreurContact.html » (qui se trouve dans la racine du site web), sinon sers l'URL qui t'es demandée. Comme la page existera toujours, c'est toujours elle qui sera servie.

    December 21, 2020 at 11:46:51 AM UTC - permalink - https://rtcamp.com/tutorials/nginx/block-wp-login-php-bruteforce-attack/
Links per page: 20 50 100
 
Mentions légales identiques à celles de mon blog | CC BY-SA 3.0

Shaarli - The personal, minimalist, super fast, database-free, bookmarking service by the Shaarli community