Le forum Discourse de freeCodeCamp.org s'effondre sous les scripts de spammeurs

Nous avons commencé à constater une charge accrue sur le forum Discourse de freeCodeCamp.org le 21 juin. Notre charge CPU moyenne a commencé à augmenter au point que l’ensemble du forum est devenu non réactif.

Notre enquête initiale

  1. Nous avons mis à jour Discourse de la version Stable vers la dernière version Tests-Passed (Bêta), ce qui est recommandé pour bénéficier de toutes les corrections de performances les plus récentes. Il ne semblait pas y avoir de grande différence.

  2. Nous avons supprimé certains anciens plugins pour aider à diagnostiquer le problème, et il ne semble pas que cela soit la cause du ralentissement.

  3. Pour écarter toute mauvaise configuration, nous avons testé cela avec le thème par défaut et vérifié les /logs. Nous avons effectivement trouvé quelques exceptions :

    Job exception: could not obtain connection from the pool within 5.000 seconds (awaited 5.006 seconds); all pooled connections were in use

Cela semble être dû au fait que l’instance est peut-être déjà surchargée par de nombreuses tâches de longue durée.

Le conteneur Discourse (upstream) s’exécute sur Digital Ocean et semble fonctionner à pleine charge sur les 6 vCPU. Il exécute actuellement la version : 2.5.0.beta7

Enquête approfondie

  1. Ce matin, nos modérateurs nous ont informés d’une augmentation significative du nombre de comptes spam, ce qui nous a amenés à penser qu’il pourrait s’agir d’une attaque ciblée.

  2. Pour tenter de mitiger le problème, nous avons mis le forum en mode lecture seule. Malgré cela, le taux d’utilisation des ressources reste très élevé, à plus de 60 %.

  3. D’après ce que nous pouvons constater : depuis le début de l’incident, nous observons un nombre plus faible de requêtes par seconde et un taux de requêtes en cours plus élevé sur notre proxy :

  1. Selon Google Analytics, le volume de trafic vers The freeCodeCamp Forum - Join the developer community and learn to code for free. n’a pas changé de manière significative.
  2. Aucune des autres applications sur le même proxy ne semble être affectée. Nos plateformes /news et /learn fonctionnent normalement.
  3. Nous avons essayé d’ajouter une limitation de débit supplémentaire sur le proxy, mais cela n’a pas fait grande différence, nous l’avons donc retirée (pour permettre aux utilisateurs normaux de continuer à accéder à notre site).
  4. Nous avons également déplacé nos redirections de l’ancien forum.freecodecamp.com hors du forum .org, car nous avons remarqué que cet ancien sous-domaine (que nous n’utilisons plus depuis 2,5 ans) a connu un pic important de trafic à partir de quelques jours.

Pour l’instant, avec environ 400 utilisateurs en temps réel, les statistiques ressemblent à ceci :

Observations sur le comportement du spammeur

Les spammeurs semblent exécuter un script qui crée de nouveaux comptes sur une instance Discourse, puis attend plusieurs jours. Ensuite, ces comptes deviennent soudainement actifs. Ils commencent à publier de nouveaux sujets avec des liens vers un site web. (Peut-être pour construire des backlinks ?)

Certains de ces comptes ont été créés dès le mois de mars.

Voici à quoi ressemble l’un de leurs messages de spam, bien qu’il existe de nombreuses variantes, pointant vers de nombreux sites web :

Toute suggestion sera la bienvenue ici.

14 « J'aime »

Cela mérite beaucoup plus d’attention

Merci beaucoup pour le partage !

L’une de mes instances Discourse préférées, freeCodeCamp, a ralenti jusqu’à s’arrêter ces deux derniers jours.

5 « J'aime »

Je ne pense pas que la charge élevée soit principalement causée par des spammeurs.
Voici pourquoi :

L’utilisation normale de Discourse (sans API) ne fonctionne qu’avec les navigateurs modernes car elle dépend fortement de JavaScript. Google Analytics utilise également JavaScript pour collecter diverses informations sur les utilisateurs (et n’a pas besoin d’un support JavaScript moderne pour exécuter le code d’analyse). Si Google Analytics ne peut pas détecter l’activité des utilisateurs, alors Discourse ne devrait pas non plus pouvoir servir son contenu et ses fonctionnalités.

Voici une capture d’un bot lorsque j’utilise une ancienne bibliothèque de navigateur sans tête (phantomjs) pour accéder au site Discourse :

Et voici avec une bibliothèque plus moderne (puppeteer) :

  1. C’est étrange qu’une personne puisse publier des réponses sur Discourse (sans API) alors que Google Analytics ne les détecte pas.
  2. Habituellement, les spammeurs utilisent des proxys publics pour faire des choses malveillantes, et je pense que votre Cloudflare est assez intelligent pour bloquer les “mauvais visiteurs” avant qu’ils n’atteignent réellement votre application.

Avez-vous observé une file d’attente élevée des tâches dans le processus Sidekiq ?

2 « J'aime »

Cela ignore le fait que plus de la moitié des personnes dans les communautés technologiques utilisent une extension de blocage de publicités, qui bloque toujours Google Analytics par défaut.

18 « J'aime »

Y compris le nouveau macOS.

Ce n’était qu’une méchante rumeur. Voir ci-dessous.

5 « J'aime »

Akismet est-il activé ? Pouvez-vous activer la modération pour le niveau de confiance 1 ?

2 « J'aime »

Oui, nous avons effectivement constaté que les jobs fluctuaient, ce qui faisait partie de notre enquête initiale. Nous avons ensuite révoqué toutes les clés des utilisateurs.

Cela n’a toutefois pas semblé avoir d’impact sur la stabilité.

Nous travaillons cependant en étroite collaboration avec l’équipe de Discourse pour résoudre ce problème.

5 « J'aime »

Je ne vois pas pourquoi il y aurait de l’étrange à ce qu’il puisse y avoir des requêtes sans que les analyses basées sur JavaScript ne montrent quoi que ce soit.

Le spammeur peut utiliser les mêmes points de terminaison que ceux utilisés par JavaScript, mais sans JavaScript. Aucune analyse basée sur JavaScript ne se déclencherait.

3 « J'aime »

Cela est beaucoup plus probablement dû à une interaction avec un plugin ou à une mauvaise configuration de proxy. Nous ne constatons aucun succès de la part des « spammeurs » sur notre hébergement.

Étant donné qu’il s’agit d’un site de codage, il pourrait aussi s’agir de « codeurs » essayant de faire quelque chose d’étrange sur le forum ?

Mais non — les spammeurs ne sont pas un problème généralisé sur les milliers de sites que nous hébergeons.

3 « J'aime »

Oui, je penche pour un problème de configuration (mauvaise configuration d’un plugin ou du proxy) ou bien quelqu’un qui perturbe le forum.

La seconde hypothèse est plus probable au vu de tous les schémas observés.

D’après ce que j’ai remarqué, les comptes spam ont été créés sur une longue période et ils tentent d’ajouter des liens (pour obtenir des backlinks ?) dans leurs biographies, ainsi que toutes sortes de choses étranges.

De plus, il pourrait y avoir du scraping, car nous avons mis le site en lecture seule, configuré un cache au niveau du proxy et mis en place une limitation du débit. L’utilisation des ressources reste élevée, même côté conteneur en amont.

Cependant, je ne rejetterais pas l’idée que notre configuration soit défectueuse : nous utilisons des sous-chemins et Cloudflare au-dessus de notre proxy inverse, ce qui, traditionnellement, n’est pas la configuration la plus efficace recommandée par Discourse.

1 « J'aime »

image

Un taux de vol de 42,5 % est vraiment élevé, même si vous êtes celui qui cause les problèmes sur cet hyperviseur. Cela ressemble à un « voisin bruyant » à mes yeux. Si j’étais vous, je contacterais DO pour leur demander de déplacer le droplet vers un autre hyperviseur.

10 « J'aime »

Je suis sûr que vous le faites probablement déjà, mais pour être certain, je vous suggère de surveiller les connexions TCP/UDP ouvertes, résumées par adresse IP, au niveau du système d’exploitation. Si la charge CPU est élevée, cela devrait vous montrer un grand nombre de connexions ouvertes vers le serveur web.

Y a-t-il un schéma étrange dans production.log ?

1 « J'aime »
4 « J'aime »

Salut Quincy @ossia

Faisons un petit pas en arrière un instant et examinons cela d’un point de vue professionnel en cybersécurité, sans spéculation ni approche du genre « se raccrocher à des brins de paille ».

Le concept clé dans toutes les tâches de cybersécurité est la « conscience situationnelle », donc dans ce cas, on parle de « conscience situationnelle cybernétique » (CSA).

Pour savoir « ce qui se passe » de manière définitive, vous devez développer la meilleure connaissance situationnelle possible, sans spéculation ni supposition. Juste les faits.

Comment faire cela ?

Eh bien, très brièvement :

Eh bien, très brièvement :

Nous procédons en fusionnant les informations provenant de tous nos capteurs, et pour les applications web, cela provient généralement des fichiers journaux (logs) et des données de session. Je ne pense pas (à première vue) que Discourse conserve les informations de session dans la base de données PG (la dernière fois que j’ai vérifié, il n’y avait pas de table de session comme dans certaines applications web LAMP), mais ce n’est absolument pas un obstacle.

Vous avez presque tout ce dont vous avez besoin dans les fichiers journaux nginx à la fois pour votre proxy inverse situé en dehors du conteneur (je me souviens avoir lu dans ce sujet que vous utilisiez nginx comme proxy) et les mêmes informations de journalisation se trouvent également dans le conteneur. Dans les deux configurations, le fichier journal se trouve ici, dans la configuration standard OOTB :

Voici un exemple dans l’une de nos configurations (en dehors du conteneur) pour le proxy inverse :

# cd /var/log/nginx
# ls -l 
total 779964
-rw-r----- 1 www-data adm         0 Jun 17 06:25 access.log
-rw-r----- 1 www-data adm 660766201 Jun 25 18:26 access.log.1
-rw-r----- 1 www-data adm 107367317 Jun 17 03:18 access.log.2.gz
-rw-r----- 1 www-data adm  21890638 May 21 03:08 access.log.3.gz
-rw-r----- 1 www-data adm   7414232 May  5 07:26 access.log.4.gz
-rw-r----- 1 www-data adm     63289 Apr 18 09:12 access.log.5.gz
-rw-r----- 1 www-data adm         0 Jun 17 06:25 error.log
-rw-r----- 1 www-data adm    904864 Jun 25 18:19 error.log.1
-rw-r----- 1 www-data adm     96255 Jun 17 03:17 error.log.2.gz
-rw-r----- 1 www-data adm     79065 May 21 02:58 error.log.3.gz
-rw-r----- 1 www-data adm     70799 May  5 06:54 error.log.4.gz
-rw-r----- 1 www-data adm      1977 Apr 18 05:49 error.log.5.gz

Voici les mêmes informations de base de journalisation à l’intérieur du conteneur Discourse :

# cd /var/discourse/
# ./launcher enter socket
# cd /var/log/nginx
# ls -l
total 215440
-rw-r--r-- 1 www-data www-data  87002396 Jun 25 18:28 access.log
-rw-r--r-- 1 www-data www-data 101014650 Jun 25 08:02 access.log.1
-rw-r--r-- 1 www-data www-data   8217731 Jun 24 08:02 access.log.2.gz
-rw-r--r-- 1 www-data www-data   6972317 Jun 23 07:53 access.log.3.gz
-rw-r--r-- 1 www-data www-data   3136381 Jun 22 07:50 access.log.4.gz
-rw-r--r-- 1 www-data www-data   2661418 Jun 21 07:45 access.log.5.gz
-rw-r--r-- 1 www-data www-data   5098097 Jun 20 07:38 access.log.6.gz
-rw-r--r-- 1 www-data www-data   6461672 Jun 19 07:40 access.log.7.gz
-rw-r--r-- 1 www-data www-data         0 Jun 25 08:02 error.log
-rw-r--r-- 1 www-data www-data         0 Jun 24 08:02 error.log.1
-rw-r--r-- 1 www-data www-data        20 Jun 23 07:53 error.log.2.gz
-rw-r--r-- 1 www-data www-data       254 Jun 23 02:36 error.log.3.gz
-rw-r--r-- 1 www-data www-data        20 Jun 21 07:45 error.log.4.gz
-rw-r--r-- 1 www-data www-data        20 Jun 20 07:38 error.log.5.gz
-rw-r--r-- 1 www-data www-data        20 Jun 19 07:40 error.log.6.gz
-rw-r--r-- 1 www-data www-data       274 Jun 18 15:40 error.log.7.gz

Note : Ces informations « dans le conteneur » sont également disponibles depuis l’extérieur du conteneur sur le volume partagé.

Par conséquent (et pour garder cette réponse courte), @ossia, presque tout ce dont vous avez besoin pour acquérir une connaissance situationnelle de ce qui se passe se trouve dans ces fichiers journaux robustes. Aucune spéculation n’est nécessaire. Les données sont toutes là.

Il y a encore plus d’excellentes données disponibles dans le journal Rails, par exemple. Sur l’une de nos configurations, voici le journal de production Rails :

tail -f /var/discourse/shared/socket/log/rails/production.log

Le journal Rails contient également beaucoup d’excellentes informations de journalisation des utilisateurs, par exemple :

Started GET "/embed/comments?topic_id=378686" for 73.63.114.60 at 2020-06-25 18:36:15 +0000
Started GET "/embed/comments?topic_id=378686" for 195.184.106.202 at 2020-06-25 18:36:16 +0000
Started GET "/embed/comments?topic_id=378686" for 17.150.212.174 at 2020-06-25 18:36:16 +0000
Started GET "/embed/comments?topic_id=378686" for 76.235.99.73 at 2020-06-25 18:36:18 +0000
Started GET "/embed/comments?topic_id=378686" for 124.253.211.42 at 2020-06-25 18:36:19 +0000
Started GET "/embed/comments?topic_id=378686" for 103.96.30.11 at 2020-06-25 18:36:21 +0000
Started GET "/embed/comments?topic_id=378686" for 72.191.206.59 at 2020-06-25 18:36:22 +0000
Started GET "/embed/comments?topic_id=378686" for 68.252.68.76 at 2020-06-25 18:36:23 +0000
Started GET "/embed/comments?topic_id=378686" for 69.17.252.83 at 2020-06-25 18:36:23 +0000
Started GET "/embed/comments?topic_id=378686" for 98.109.33.230 at 2020-06-25 18:36:24 +0000

Note : Ici (ci-dessus, à titre d’exemple), nous voyons les adresses IP des clients récupérant le code intégré de Discourse depuis un autre serveur.

La tâche à accomplir....

Revenons à la tâche à accomplir, le « truc » consiste à dépasser les spéculations et les suppositions, et à effectuer le divertissant (1) filtrage / nettoyage des données, (2) fusion des données et (3) analyse de vos données de capteurs (fichiers journaux) afin de créer (4) la conscience situationnelle (SA) de ce qui se passe sur votre site.

Pour les anciennes applications LAMP, j’ai en fait un code personnalisé que j’ai écrit il y a des années et qui écrit toutes ces informations dans une table de base de données et effectue l’analyse en temps réel, en comptant les « clics » par adresse IP (comme exemple), ce qui me permet de voir rapidement ce qui et qui, et d’où, accède au site, car cela nécessite un certain code pour effectuer ce type de nettoyage, de filtrage et de fusion des données. (Utile lors d’attaques DDoS et d’activités de bots malveillants, par exemple).

Ce n’est pas un problème pour vous @ossia car vous êtes freeCodeCamp.org, vous avez donc à la fois les connaissances pour trouver d’excellents outils d’analyse de fichiers journaux (il y en a beaucoup dans le cyberunivers) et/ou créer votre propre code personnalisé pour effectuer l’analyse rapidement et facilement en fonction du scénario que vous souhaitez comprendre (votre sujet et votre problème).

J’ai écrit mon code personnalisé pour une ancienne application LAMP legacy en quelques heures il y a de nombreuses années, et je ne suis en aucun cas un génie de la programmation, même si je suis parfois qualifié de « légende » par beaucoup dans le domaine de la cybersécurité, LOL :slight_smile:

Pour résumer....

Eh bien, pour résumer…

Vous disposez de toutes les données nécessaires pour créer une connaissance situationnelle approfondie de « ce qui se passe » sur votre site. Vous pouvez créer cette SA en nettoyant, filtrant, fusionnant et effectuant une analyse de base de vos données de fichiers journaux. Il existe des outils qui peuvent aider, mais je trouve toujours plus facile d’écrire rapidement du code personnalisé en fonction de l’objectif de l’analyse (analyse dépendante), YMMV. Mais vous pouvez facilement le faire car vous êtes freeCodeCamp.org et disposez de nombreuses compétences techniques.

Je vous recommande de vous éloigner de la tentative d’obtenir une SA à partir de Google Analytics et d’autres applications tierces basées sur JavaScript. Rien ne vaut vos propres fichiers journaux web (et les données de session de la base de données si vous les avez), et vous n’avez pas à vous soucier de « ce qui peut ou non être bloqué », etc. Vos fichiers journaux de serveur web contiennent les données nécessaires pour obtenir la CSA dont vous avez besoin (et peuvent également être personnalisés si nécessaire).

Dans certains de mes codes CSA, j’intercepte en fait les informations de session et les informations de journalisation des requêtes HTTP non enregistrées par nginx, apache2 et d’autres serveurs web (pour des informations supplémentaires) ; mais je n’ai pas écrit ce type de code pour Discourse (pour l’instant) car je ne suis pas « aussi simple que la tarte » en tant que développeur de plugins Discourse (comme les gourous de l’équipe Meta Discourse ici) que je le suis avec les applications LAMP, n’ayant commencé avec Discourse que depuis quelques mois et n’ayant encore écrit aucun code personnalisé de CSA pour Discourse (et essayant de coder moins cette année, pour être honnête).

La CSA est basée sur la fusion des données de capteurs, et de la CSA découle la connaissance nécessaire pour comprendre quelles actions vous devez entreprendre pour remédier à tout problème de cybersécurité.

Tout le meilleur dans votre quête et j’espère que cela vous aidera à avoir plus de repos :slight_smile

Salutations !


Référence CSA originale (historique) :

Référence CSA originale (historique) :

https://www.researchgate.net/publication/220420389_Intrusion_Detection_Systems_and_Multisensor_Data_Fusion

(Référence uniquement pour les personnes intéressées par les origines et la technologie de base de la CSA)

13 « J'aime »

Merci pour votre travail acharné, équipe !

& merci vraiment, vraiment, vraiment beaucoup
pour avoir simplifié cette
barre d’outils double :nauseated_face:

4 « J'aime »

Je vais simplement boucler la boucle ici.

Discourse héberge désormais https://forum.freecodecamp.org/. Le site est ultra-réactif, les scripts de spammeurs ne provoquent plus même un léger ralentissement. Nous restons un peu incertains quant au problème qu’il rencontrait sur Digital Ocean : il pourrait s’agir d’un voisin bruyant, la machine pourrait être sous-dimensionnée, ou il pourrait y avoir eu des « gremlins » matériels. Nous ne sommes pas sûrs. Mais le problème initial est désormais résolu à 100 % et la communauté est très satisfaite.

16 « J'aime »