Construire un scanner de ports TCP basé sur le web

HTMLHTMLBeginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans le projet précédent, nous avons développé un scanner de ports TCP en Python qui utilisait le threading et les sockets pour effectuer des scans de ports. Bien que cela soit efficace, il existe des possibilités d'amélioration en utilisant des packages tiers.

Dans ce projet, nous allons améliorer notre scanner de ports en intégrant la bibliothèque python-nmap, offrant des capacités de scan plus robustes. De plus, nous allons construire une application web utilisant Flask pour fournir une interface utilisateur conviviale pour notre scanner. Ce projet étape par étape vous guidera tout au long du processus, vous assurant de pouvoir suivre et de vous appuyer sur vos connaissances existantes.

👀 Aperçu

🎯 Tâches

Dans ce projet, vous allez apprendre :

  • Comment configurer un projet Flask et organiser sa structure
  • Comment utiliser Flask-WTF pour créer et gérer de manière sécurisée des formulaires web
  • Comment implémenter des routes Flask pour gérer les requêtes et les soumissions de pages web
  • Comment utiliser la bibliothèque Nmap en Python pour effectuer des scans de ports
  • Comment afficher dynamiquement les résultats du scan sur une page web en utilisant Flask et des modèles HTML
  • Comment appliquer le CSS Tailwind de base pour améliorer la conception de l'interface utilisateur

🏆 Réalisations

Après avoir terminé ce projet, vous serez capable de :

  • Monter en épingle une compréhension de base du développement web avec Flask, y compris la routage, la présentation de modèles et la gestion des formulaires
  • Appliquer une expérience pratique de l'intégration de scripts Python avec des interfaces web
  • Montrer une maîtrise dans l'utilisation de la bibliothèque Nmap pour les tâches de scan réseau
  • Utiliser Flask-WTF pour la création et la validation de formulaires dans une application web
  • Mettre en évidence une familiarité avec l'utilisation du CSS Tailwind pour la mise en forme des pages web et l'amélioration de la conception de l'interface utilisateur
  • Créer une application web fonctionnelle qui interagit avec des scripts Python de fond pour effectuer des scans réseau

Implémentation de la page d'accueil

Pour commencer, ouvrez templates/index.html et ajoutez le code suivant pour vous assurer que le formulaire est correctement configuré pour soumettre des requêtes de scan :

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>TCP Port Scanner</title>
    <link
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css"
      rel="stylesheet"
    />
    <script>
      function updateButton() {
        var submitButton = document.getElementById("scanButton");
        submitButton.value = "Scanning...";
        submitButton.classList.add("cursor-not-allowed", "opacity-50");
        submitButton.disabled = true;
      }
    </script>
  </head>
  <body class="bg-gray-100 flex items-center justify-center h-screen">
    <div class="bg-white p-8 rounded-lg shadow-md">
      <h1 class="text-2xl font-bold mb-4">TCP Port Scanner</h1>
      <form action="" method="post" class="space-y-4" onsubmit="updateButton()">
        {{ form.hidden_tag() }}
        <div>
          {{ form.host.label(class="block text-sm font-medium text-gray-700") }}
          {{ form.host(class="mt-1 block w-full rounded-md border-gray-300
          shadow-sm focus:border-indigo-500 focus:ring-indigo-500") }}
        </div>
        <div>
          {{ form.ports.label(class="block text-sm font-medium text-gray-700")
          }} {{ form.ports(class="mt-1 block w-full rounded-md border-gray-300
          shadow-sm focus:border-indigo-500 focus:ring-indigo-500") }}
        </div>
        <div>
          {{ form.submit(id="scanButton", class="w-full flex justify-center py-2
          px-4 border border-transparent rounded-md shadow-sm text-sm
          font-medium text-white bg-indigo-600 hover:bg-indigo-700
          focus:outline-none focus:ring-2 focus:ring-offset-2
          focus:ring-indigo-500") }}
        </div>
      </form>
    </div>
  </body>
</html>

Le modèle index.html fournit une interface utilisateur conviviale pour soumettre un hôte et une plage de ports à scanner. Le formulaire HTML comprend deux principaux champs : l'un pour l'adresse hôte et l'autre pour spécifier la plage de ports.

Flask-WTF, une extension Flask pour travailler avec WTForms, est utilisée pour afficher ces champs dans le modèle. La soumission du formulaire est améliorée par une petite fonction JavaScript updateButton() qui change le texte du bouton de soumission en "Scanning..." lors de la soumission du formulaire. Cette rétroaction visuelle informe l'utilisateur que leur requête de scan est en cours de traitement.

La mise en forme de la page est gérée par Tailwind CSS, un framework CSS axé sur les utilitaires qui permet un développement d'interface utilisateur rapide.

✨ Vérifier la solution et pratiquer

Implémentation de la page des résultats du scan

Ouvrez templates/results.html et assurez-vous d'inclure le code suivant pour afficher les résultats du scan :

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Scan Results</title>
    <link
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css"
      rel="stylesheet"
    />
  </head>
  <body class="bg-gray-100">
    <div class="flex flex-col items-center justify-center min-h-screen">
      <div class="bg-white p-8 rounded-lg shadow-lg w-full max-w-4xl">
        <h1 class="text-2xl font-bold mb-4 text-center">
          Scan Results for {{ host }}
        </h1>
        <div class="overflow-x-auto">
          <table class="table-auto w-full text-left whitespace-no-wrap">
            <thead>
              <tr
                class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50"
              >
                <th class="px-4 py-3">PORT</th>
                <th class="px-4 py-3">STATE</th>
                <th class="px-4 py-3">SERVICE</th>
                <th class="px-4 py-3">VERSION</th>
              </tr>
            </thead>
            <tbody
              class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800"
            >
              {% for result in scan_results %}
              <tr class="text-gray-700 dark:text-gray-400">
                <td class="px-4 py-3 text-sm">{{ result.port }}/tcp</td>
                <td class="px-4 py-3 text-sm">{{ result.state }}</td>
                <td class="px-4 py-3 text-sm">{{ result.name }}</td>
                <td class="px-4 py-3 text-sm">
                  {{ result.product }} {{ result.version }} {{ result.extra }}
                </td>
              </tr>
              {% endfor %}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </body>
</html>

Le modèle results.html est responsable d'afficher le résultat du scan de ports. Il présente les résultats du scan sous forme de tableau, en listant chaque port avec son état correspondant, le nom du service et la version du service le cas échéant.

Ce modèle utilise Tailwind CSS pour la mise en forme, assurant que la page des résultats est à la fois réactive et visuellement attrayante. L'utilisation du moteur de templating Jinja2 (intégré avec Flask) permet le rendu de contenu dynamique, où les résultats du scan sont transmis de l'application Flask au modèle et parcourus pour alimenter le tableau.

✨ Vérifier la solution et pratiquer

Initialiser l'application Flask

Dans cette étape, nous allons créer le script principal Python pour notre application Flask.

Ajoutez le code suivant dans app.py :

## Import necessary modules
from flask import Flask, render_template, request, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired, Regexp
import nmap

## Initialize Flask app
app = Flask(__name__)
app.config['SECRET_KEY'] = 'labex'

## Initialize Nmap PortScanner
nm = nmap.PortScanner()

## Define Flask-WTF form for scanning inputs
class ScanForm(FlaskForm):
    host = StringField('Host', validators=[DataRequired()])
    ports = StringField('Port Range', validators=[DataRequired(), Regexp(r'^\d+-\d+$', message="Format must be start-end")])
    submit = SubmitField('Scan')

Dans cette étape, l'application Flask et la classe de formulaire sont initialisées.

Le script app.py commence par importer les modules nécessaires, y compris Flask lui-même, Flask-WTF pour la gestion des formulaires et Nmap pour effectuer des scans de ports. L'instance d'application Flask est créée et configurée avec une clé secrète pour protéger contre les attaques CSRF.

La classe ScanForm définit les champs de formulaire pour les entrées d'hôte et de plage de ports, en utilisant des validateurs pour s'assurer que les données sont fournies et au bon format (pour la plage de ports, en particulier).

Remplacez 'your_secret_key' par une véritable clé secrète utilisée pour protéger les formulaires contre les attaques CSRF.

✨ Vérifier la solution et pratiquer

Gérer la route d'accueil

Dans cette étape, nous allons gérer la route d'accueil où les utilisateurs peuvent soumettre l'hôte et la plage de ports qu'ils souhaitent scanner. Ajoutez la fonction suivante à app.py :

## Define route for the index page
@app.route('/', methods=['GET', 'POST'])
def index():
    form = ScanForm()  ## Instancier le formulaire
    if form.validate_on_submit():
        ## Récupérer les données du formulaire
        host = form.host.data
        ports = form.ports.data  ## Format : "start-end"
        ## Rediriger vers la route de scan avec les données du formulaire
        return redirect(url_for('scan', host=host, ports=ports))
    ## Afficher le modèle de la page d'accueil avec le formulaire
    return render_template('index.html', form=form)

Cette partie du script app.py définit la route pour la page d'accueil de l'application web. La fonction index() affiche le modèle index.html ainsi que l'instance ScanForm.

Lorsque le formulaire est soumis et passe les vérifications de validation, la fonction redirige l'utilisateur vers la route de scan, en passant les données du formulaire (hôte et plage de ports) via des paramètres d'URL. Cette redirection lance le processus de scan.

✨ Vérifier la solution et pratiquer

Implémenter la route de scan

Cette étape consiste à créer une route pour effectuer le scan réel et afficher les résultats. Ajoutez la fonction suivante à app.py :

## Define route for the scan results
@app.route('/scan')
def scan():
    ## Récupérer l'hôte et les ports à partir de la chaîne de requête
    host = request.args.get('host')
    ports = request.args.get('ports')
    ## Effectuer le scan à l'aide de Nmap
    nm.scan(hosts=host, ports=ports, arguments='-sV')  ## -sV pour la détection du service/version
    scan_results = []

    ## Traiter les résultats du scan et les stocker dans une liste
    for host in nm.all_hosts():
        for proto in nm[host].all_protocols():
            lport = nm[host][proto].keys()
            for port in lport:
                service = nm[host][proto][port]
                scan_results.append({
                    'port': port,
                   'state': service['state'],
                    'name': service.get('name', 'Unknown'),
                    'product': service.get('product', ''),
                   'version': service.get('version', ''),
                    'extra': service.get('extrainfo', '')
                })

    ## Afficher le modèle de la page des résultats avec les résultats du scan
    return render_template('results.html', scan_results=scan_results, host=host)

La fonction scan() gère la route responsable d'effectuer le scan de ports réel et d'afficher les résultats. Elle récupère l'hôte et la plage de ports à partir des paramètres de chaîne de requête passés dans l'URL.

En utilisant l'instance Nmap PortScanner, elle effectue un scan sur l'hôte et les ports spécifiés, avec l'argument -sV pour détecter les versions des services.

Les résultats du scan sont traités et organisés en une liste de dictionnaires, chacun contenant des détails sur un port scanné. Ces détails sont ensuite passés au modèle results.html, où ils sont affichés à l'utilisateur.

✨ Vérifier la solution et pratiquer

Exécuter l'application Flask

Avec tous les composants en place, vous êtes maintenant prêt à exécuter l'application Flask et à faire vivre votre scanner de ports TCP. Le dernier morceau de code nécessaire dans votre app.py assure que l'application Flask ne sera exécutée que si le script est exécuté directement, et non s'il est importé en tant que module dans un autre script. C'est un modèle courant dans les applications Python qui incluent un script exécutable.

Placez le fragment de code suivant à la fin de votre fichier app.py :

if __name__ == '__main__':
    app.run(debug=True, port=8080, host='0.0.0.0')

Ce code indique à Flask de démarrer votre application avec le débogage activé, ce qui facilite la détection d'erreurs. L'application écoutera sur toutes les interfaces réseau (host='0.0.0.0') et utilisera le port 8080. Le mode débogage ne devrait être utilisé que pendant le développement, car il peut être insecuritaire d'utiliser en environnement de production.

Pour exécuter votre application Flask, assurez-vous d'être dans le répertoire du projet où se trouve app.py. Ensuite, exécutez la commande suivante dans le terminal :

python app.py

Accédez à l'onglet Web 8080 pour accéder au scanner de ports TCP. Vous pouvez maintenant entrer un hôte et une plage de ports à scanner, et voir les résultats sur la page des résultats.

Les ports 22 et 3306 sont respectivement associés de manière ubiquitaire aux services SSH et MySQL, le port 3000 est utilisé pour les environnements WebIDE.

✨ Vérifier la solution et pratiquer

Récapitulatif

Dans ce projet, vous avez appris à construire un scanner de ports TCP basé sur le web, simple mais puissant, à l'aide de Flask et de Nmap. Nous avons commencé par configurer l'environnement du projet et installer les dépendances nécessaires. Nous avons ensuite progressé dans la création de l'application Flask, la gestion des soumissions de formulaires, la réalisation du scan de ports et l'affichage des résultats de manière conviviale. Ce projet est un excellent introduction au développement web avec Flask et au balayage réseau avec Nmap, offrant une application pratique qui combine les deux compétences.