SitInCloud

Evarli étudiant Développeur Informatique Logo stage SitInCloud
Développement d'un Moteur de Recherche sur le Darkweb

Objectifs

Dès le début de mon stage, les objectifs étaient clairs : contribuer au développement d’un moteur de recherche sur le darkweb, en intervenant à la fois sur le Front End et le Back End. Cette mission m’a offert l’opportunité de consolider mes connaissances académiques tout en relevant des défis concrets dans le domaine du développement web.

Introduction

Au sein de Sitincloud, j’ai eu l’opportunité unique de participer à un stage captivant de sept semaines, du 8 janvier au 23 février, au cours de ma deuxième année de BTS Services Informatiques aux Organisations (SIO). Ce stage s’est révélé être une immersion intense dans le monde du développement informatique, où j’ai pu mettre en œuvre mes compétences et contribuer activement à la création d’un moteur de recherche sur le darkweb.

Travail Réalisé

Back End

back end

Du côté du Back End, j’ai participé à la mise en place d’une API fondamentale pour le fonctionnement du moteur de recherche. Cette phase a impliqué la résolution de problèmes complexes et le développement de solutions robustes pour garantir un fonctionnement fluide. Les détails techniques de cette partie du projet seront explorés avec des exemples concrets.

back end
front end

Front End

front end

Dans le domaine du Front End, j’ai joué un rôle actif dans la conception et le développement de l’interface utilisateur du moteur de recherche. L’utilisation de Vue.js a été au cœur de cette phase, me permettant de créer des fonctionnalités interactives et dynamiques. Des extraits de code, des captures d’écran et des démonstrations pratiques illustreront plus en détail les aspects spécifiques que j’ai contribués.

Début du projet, analyse de la demande.

dessin

L’interaction a débuté par un entretien approfondi de deux heures, au cours duquel nous avons exploré en détail les objectifs de l’application, son contenu et les exigences spécifiques du client. La requête du client incluait la création d’un moteur de recherche capable de fournir des résultats sous forme d’URLs accompagnées de descriptions. De plus, le client souhaitait disposer de fonctionnalités de filtrage et de la possibilité de masquer les URLs récurrents. Après une compréhension approfondie des besoins, j’ai procédé à la conception du schéma du moteur de recherche, offrant ainsi au client une visualisation concrète de la solution envisagée.

Ce stage chez Sitincloud a été une véritable école pour moi. J’y ai développé mes compétences techniques, renforcé ma compréhension du développement Full Stack, et appris à résoudre des problèmes réels en équipe. La collaboration avec des professionnels chevronnés m’a également permis d’acquérir une vision plus approfondie des bonnes pratiques de l’industrie.

dessin
schéma interface global

Schéma aperçut général de l'interface

pop filtres

Schéma pop-up filtres et urls masqués

liste taches

Exemples de liste des tâches

Développement sur Vue.js

Logo Vuejs

Pour relever ce défi, nous avons opté pour Vue.js, une nouvelle expérience pour moi. Le choix de cette technologie a ajouté une dimension stimulante au projet, nécessitant une courbe d’apprentissage rapide. Grâce à ma capacité d’adaptation et à la flexibilité de Vue.js, j’ai pu rapidement assimiler les concepts et mettre en œuvre efficacement les fonctionnalités demandées. Cette immersion dans une technologie nouvelle a enrichi mon expertise tout en offrant au projet une approche moderne et dynamique.

Logo Vuejs

Trois langages de programmation utilisés

html logo

HTML

css logo

CSS

js logo

JavaScript

Front End

Back End

Coup d'œil sur l'arborescence

Aperçu de l'interface

L’interface est dotée d’une barre de recherche et d’une barre latérale comprenant plusieurs fonctionnalités. Cette dernière comporte un bouton permettant de télécharger les liens au format CSV, d’afficher les URL en doublons, et de révéler les URL masquées. Un autre bouton donne accès à une interface dédiée à la création d’alertes basées sur des mots-clés, similaire au système de Google Alertes. Une section d’aide et de documentation est également disponible.

De plus, l’interface intègre un bouton permettant de mettre en place des filtres précis, en excluant certains mots ou en activant des pop-ups. Des boutons de connexion et de déconnexion sont également présents pour assurer la gestion des sessions utilisateur. En coulisses, l’interface repose sur des dizaines de fonctions JavaScript qui interagissent de manière dynamique avec les préférences de l’utilisateur. Ci-dessous un exemple concret de cette fonctionnalité est la gestion des filtres via des fenêtres contextuelles.

Exemple de code sur le pop-up Filtres d'exclusion

interface filtre

HTML

				
					        <v-dialog v-model="showFilterDialog" max-width="600px">
    <v-card theme="dark">
      <v-card-title>
        Filtres d'exclusion
        <v-spacer></v-spacer>
        <v-btn icon class="close-btn" @click="showFilterDialog = false">
          <v-icon>mdi-close</v-icon>
        </v-btn>
      </v-card-title>
      <v-card-text>
        <div class="d-flex align-center">
          <v-text-field
            v-model="newCustomExclusion"
            label="Mot à exclure de la recherche"
            single-line
            hide-details
            class="flex-grow-1"
            @keydown.enter="addCustomExclusion"
            @input="handleInput"
          ></v-text-field>
          <v-btn
            icon
            class="ml-2 custom-color-btn"
            @click="addCustomExclusion"
          >
            <v-icon>mdi-plus</v-icon>
          </v-btn>
        </div>
        <v-chip
          v-for="(exclusion, index) in customExclusions"
          :key="generateChipKey(exclusion, index)"
          closable
          icon
          small
          class="my-1 mot-exclusion"
          @click:close="removeCustomExclusion(index)"
        >
          {{ filterCustomExclusion(exclusion) }}
        </v-chip>

        <div class="filter-prepared-style">
          <v-checkbox
            v-model="emailFilter"
            label="Masquer les URLs contenant des emails"
          ></v-checkbox>
        </div>
        <v-row>
          <v-text-field
            type="date"
            v-model="datedebut"
            :max="datefin"
            clearable
            @input="updateDateDebut"
          ></v-text-field>
          <v-spacer></v-spacer>
          <v-text-field
            type="date"
            v-model="datefin"
            :min="datedebut"
            clearable
            @input="updateDateFin"
          ></v-text-field>
        </v-row>
      </v-card-text>

      <v-card-actions>
        <v-btn text @click="resetFilter('all')">Réinitialiser</v-btn>
        <v-spacer></v-spacer>
        <v-btn color="#ed592e" text @click="applyFilters">Appliquer</v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
				
			

JavaScript

				
					addCustomExclusion() {
  if (this.newCustomExclusion.trim()) {
    if (!this.customExclusions.includes(this.newCustomExclusion.trim())) {
      this.customExclusions.push(this.newCustomExclusion.trim());
    }
    this.newCustomExclusion = "";
  }
  this.isFilterModified = true;
}

handleInput() {
  // Convertir le texte en minuscules
  this.newCustomExclusion = this.newCustomExclusion.toLowerCase();
}

generateChipKey(exclusion, index) {
  return `chip-${exclusion}-${index}`;
}

removeCustomExclusion(index) {
  if (index >= 0 && index < this.customExclusions.length) {
    this.customExclusions.splice(index, 1);
  }
  this.isFilterModified = true;
}

filterCustomExclusion(value) {
  return value.toLowerCase();
}

updateDateDebut() {
  // Met à jour la date de fin minimale lorsque la date de début est modifiée
  this.$nextTick(() => {
    this.saveDatesToLocalStorage();
  });
}

updateDateFin() {
  // Met à jour la date de début maximale lorsque la date de fin est modifiée
  this.$nextTick(() => {
    this.saveDatesToLocalStorage();
  });
}

resetFilter(filterName) {
  if (filterName === "all") {
    this.customExclusions = [];
    this.emailFilter = false;
    this.filtersApplied = false;
  } else {
    if (filterName === "emailFilter") {
      this.emailFilter = false;
    }
    this.filtersApplied =
      this.emailFilter || this.customExclusions.length > 0;
  }
  if (filterName === "currentFilteredUrl") {
    this.currentFilteredUrl = "";
    this.totalFilteredOccurences = 0;
  }
  this.isFilterModified = true;
}

applyFilters() {
  // Vérifier la valeur de emailFilter dans le localStorage
  const storedEmailFilter = localStorage.getItem("emailFilter");
  const storedDateFilter = localStorage.getItem("dateFilter");
  if (storedEmailFilter !== null) {
    // Mettre à jour la valeur de emailFilter
    this.emailFilter = storedEmailFilter === "true";
  }
  if (storedDateFilter !== null) {
    // Mettre à jour la valeur de emailFilter
    this.dateFilter = storedDateFilter === "true";
  }

  // Appliquer les filtres
  this.filtersApplied = true;
  this.showFilterDialog = false;
  this.resetDateFilters();
  this.updateVisibleHits();
}
				
			

Comment se passe la recherche?

Lorsque vous entrez des informations dans la barre de recherche et appuyez sur Rechercher, la fonction performSearch() est activée. Elle vérifie d’abord si une cible de recherche est spécifiée, puis examine les filtres temporels comme la date de début et de fin. Ces filtres sont cruciaux pour restreindre la recherche aux périodes pertinentes, notamment pour les ransomwares.

Si la recherche est pertinente et que des filtres sont ajustés, la fonction réinitialise l’état de la recherche et la pagination. Ensuite, elle construit l’URL de l’API dédiée à la recherche de ransomwares, en incluant les paramètres de recherche.

Elle prépare une liste de requêtes pour l’API, en utilisant les paramètres fournis comme la requête de recherche et les filtres temporels.

Une fois que le serveur répond, la fonction analyse les résultats. Si tout se passe bien, les données sont intégrées à la liste existante, puis formatées en JSON pour une lisibilité et une utilisation aisées.

En conclusion, la fonction rapporte les résultats obtenus, présentant les données en JSON en cas de succès, sinon signalant une erreur.

Comment se passe la recherche?

				
					performSearch() {
  // Initialisation des indicateurs et compte à rebours
  this.isFilterModified = false; // Indique si les filtres ont été modifiés
  this.timerCount = 30; // Compte à rebours en secondes pour la recherche

  // Vérification si la barre de recherche n'est pas vide
  if (this.search.trim()) {
    // Construction du filtre de date
    let dateFilter = "";
    if (this.datedebut != "") {
      dateFilter += " date:>=" + this.datedebut;
    }
    if (this.datefin != "") {
      dateFilter += " date:<=" + this.datefin;
    }

    // Vérification si la recherche ou le filtre de date a changé, réinitialisation si nécessaire
    if (this.search != this.searchQuery || dateFilter != this.dateFilter) {
      this.resetSearch();
    }

    // Mise à jour des paramètres de recherche
    this.dateFilter = dateFilter;
    this.clicksOnSeeMore = 0;
    this.showQuickSearch = false;
    this.searchPerformed = true;
    this.searchQuery = this.search;
    this.loading = true;
    this.error = null;

    // Options pour la requête Fetch vers l'API
    const fetchOptions = {
      method: "GET",
      headers: {
        Authorization: `Bearer ${this.token}`,
        // 'Content-Type': 'application/json' // Si nécessaire
      },
    };

    // Construction de l'URL de l'API avec les paramètres de recherche
    const apiUrl = `https://xxx.xxx.xxx.xxx/abcd/abcde`;
    const params = new URLSearchParams({
      query: this.searchQuery + dateFilter,
      max_hits: this.maxHits,
      start_offset: this.start_offset, // Utilise maintenant 'start_offset'
    }).toString();

    // Appel à l'API avec Fetch
    fetch(`${apiUrl}?${params}`, fetchOptions)
      .then((response) => {
        // Gestion de la réponse de l'API
        if (response.ok) {
          return response.json();
        } else if (response.status === 401) {
          // Erreur si l'accès est non autorisé
          throw new Error("Accès non autorisé. Veuillez-vous connecter.");
        } else {
          // Erreur si la ressource est non autorisée
          throw new Error("Unauthorized resource");
        }
      })
      .then((data) => {
        // Traitement des résultats de la recherche
        this.apiResults.hits = [...this.apiResults.hits, ...data.hits];
        this.processSearchResults(this.apiResults.hits);
        this.totalResults = data.num_hits; // Ajustement selon la structure des données

        // Mise à jour de la pagination
        this.maxVisiblePages = Math.ceil(
          this.apiResults.visible.length / this.resultsPerPage
        );
        console.log(
          " Visible page " +
            this.maxVisiblePages +
            " Current page : " +
            this.currentPage
        );
        // Ajustement de la page courante si nécessaire
        if (
          this.currentPage > this.maxVisiblePages &&
          this.maxVisiblePages > 0
        ) {
          this.currentPage = this.maxVisiblePages;
        }
      })
      .catch((error) => {
        // Gestion des erreurs
        this.error = error.message;
      })
      .finally(() => {
        // Fin de l'appel à l'API, mise à jour de l'état de chargement
        this.loading = false;
      });
  } else {
    // Réinitialisation de la recherche si la barre de recherche est vide
    this.search = "";
    this.resetSearch();
  }
}

				
			

Fonctionnalités clés

Surveillance Continue : L’outil assure une surveillance constante des activités sur le Dark Web, identifiant tout élément lié à une entreprise (par exemple).

Analyse de Vulnérabilités : En examinant les données collectées, il identifie les failles potentielles qui pourraient compromettre la réputation de de l’entreprise.

Rapports Intelligents : Des rapports détaillés et intelligibles sont générés, fournissant une vision claire des menaces détectées et des recommandations pour renforcer la sécurité.

Ce projet a été une expérience riche où j’ai eu l’opportunité d’explorer divers aspects du développement logiciel et de la gestion de projet.

Travailler au sein d’une équipe dynamique a été une expérience vraiment enrichissante. La collaboration constante a été une véritable source d’inspiration, car elle a créé un environnement propice à la créativité collective. L’échange régulier d’idées a contribué à nourrir cette dynamique, permettant ainsi le développement d’idées novatrices.

Mon stage m’a également ouvert les portes d’un monde fascinant de nouvelles technologies. Cela a constitué une véritable plongée au cœur de l’évolution technologique, élargissant significativement mes compétences et ma compréhension du paysage technologique actuel. La découverte constante de ces nouveautés a été à la fois stimulante et formatrice.

La résolution de problèmes complexes, en particulier le débogage pour assurer le bon fonctionnement du projet, est devenue une véritable passion pour moi au cours de ce stage. Trouver des solutions efficaces aux obstacles rencontrés a été à la fois gratifiant et un apprentissage constant.

Les échanges fréquents avec les clients ont joué un rôle essentiel dans mon parcours. Ils ont représenté une composante cruciale de mon rôle, me permettant de comprendre précisément leurs besoins spécifiques. Cette interaction a été la clé pour ajuster constamment l’outil en fonction des attentes et des exigences des clients.

La gestion du projet a constitué un défi stimulant, nécessitant une planification minutieuse et une coordination efficace pour garantir la livraison en temps voulu. Cette expérience a renforcé ma compréhension de l’importance de la gestion du temps et des ressources pour assurer le succès d’un projet.

Enfin, voir le projet aboutir de manière concrète, en conformité avec ma vision initiale, a été incroyablement gratifiant. Cette réalisation a consolidé ma confiance en mes capacités de conception et de mise en œuvre, soulignant l’impact positif de mes efforts dans la concrétisation d’une vision.

Fin

© Eren Varli