Создайте веб-сканер TCP-портов

HTMLHTMLBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В предыдущем проекте мы разработали сканер портов на Python, который использовал многопоточность и сокеты для сканирования TCP-портов. Хотя он был эффективным, есть возможность улучшения с использованием сторонних пакетов.

В этом проекте мы усовершенствуем наш сканер портов, интегрируя библиотеку python-nmap, которая обеспечивает более надежные возможности сканирования. Кроме того, мы построим веб-приложение с использованием Flask, чтобы предоставить пользовательский интерфейс для нашего сканера. Этот пошаговый проект проведет вас через весь процесс, обеспечивая возможность последовать и использовать имеющиеся знания.

👀 Предварительный просмотр

🎯 Задачи

В этом проекте вы научитесь:

  • настраивать проект Flask и организовывать его структуру;
  • использовать Flask-WTF для безопасного создания и обработки веб-форм;
  • реализовывать маршруты Flask для обработки запросов и отправок веб-страниц;
  • использовать библиотеку Nmap в Python для проведения сканирования портов;
  • динамически отображать результаты сканирования на веб-странице с использованием Flask и HTML-шаблонов;
  • применять базовый Tailwind CSS для улучшения дизайна фронтенда.

🏆 Достижения

После завершения этого проекта вы сможете:

  • продемонстрировать основное понимание веб-разработки с использованием Flask, включая маршрутизацию, рендеринг шаблонов и обработку форм;
  • применить практический опыт интеграции Python-скриптов с веб-интерфейсами;
  • продемонстрировать умение использовать библиотеку Nmap для сетевых задач сканирования;
  • использовать Flask-WTF для создания и проверки форм в веб-приложении;
  • показать знакомство с использованием Tailwind CSS для стилизации веб-страниц и улучшения дизайна пользовательского интерфейса;
  • создать функциональное веб-приложение, которое взаимодействует с Python-скриптами на стороне сервера для выполнения сетевых сканирований.

Реализация главной страницы

Для начала откройте templates/index.html и добавьте следующий код, чтобы убедиться, что форма правильно настроена для отправки запросов на сканирование:

<!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/tailwindcss@2.0.3/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>

Шаблон index.html предоставляет пользовательский интерфейс для отправки хоста и диапазона портов для сканирования. HTML-форма содержит два основных поля: одно для адреса хоста и другое для указания диапазона портов.

Flask-WTF, расширение Flask для работы с WTForms, используется для отображения этих полей в шаблоне. Отправка формы усовершенствована небольшой JavaScript-функцией updateButton(), которая изменяет текст кнопки отправки на "Scanning..." при отправке формы. Эта визуальная обратная связь информирует пользователя, что их запрос на сканирование обрабатывается.

Стилизация страницы обрабатывается Tailwind CSS, фреймворком CSS с приоритетом на утилиты, который позволяет быстро разрабатывать интерфейс пользователя.

✨ Проверить решение и практиковаться

Реализация страницы с результатами сканирования

Откройте templates/results.html и убедитесь, что в него включен следующий код для отображения результатов сканирования:

<!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/tailwindcss@2.0.3/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>

Шаблон results.html отвечает за отображение результата сканирования портов. Он представляет результаты сканирования в табличном формате, перечисляя каждый порт с соответствующим состоянием, именем сервиса и версией сервиса, если это возможно.

Этот шаблон использует Tailwind CSS для стилизации, обеспечивая при этом отзывчивость и привлекательный внешний вид страницы с результатами. Использование Jinja2-шаблонизатора (интегрированного с Flask) позволяет динамически рендерить содержимое, при этом результаты сканирования передаются из Flask-приложения в шаблон и перебираются для заполнения таблицы.

✨ Проверить решение и практиковаться

Инициализация Flask-приложения

В этом шаге мы создадим главный Python-скрипт для нашего Flask-приложения.

Добавьте следующий код в 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')

В этом шаге инициализируются Flask-приложение и класс формы.

Скрипт app.py начинается с импорта необходимых модулей, включая сам Flask, Flask-WTF для обработки форм и Nmap для проведения сканирования портов. Создается экземпляр Flask-приложения и настраивается с использованием секретного ключа для защиты от атак CSRF.

Класс ScanForm определяет поля формы для ввода хоста и диапазона портов, используя валидаторы для обеспечения предоставления данных и их правильного формата (особенно для диапазона портов).

Замените 'your_secret_key' на реальный секретный ключ, который используется для защиты форм от атак CSRF.

✨ Проверить решение и практиковаться

Обработка маршрута главной страницы

В этом шаге мы обработаем маршрут главной страницы, где пользователи могут отправить хост и диапазон портов, которые они хотят сканировать. Добавьте следующую функцию в app.py:

## Define route for the index page
@app.route('/', methods=['GET', 'POST'])
def index():
    form = ScanForm()  ## Instantiate the form
    if form.validate_on_submit():
        ## Get data from the form
        host = form.host.data
        ports = form.ports.data  ## Format: "start-end"
        ## Redirect to the scan route with form data
        return redirect(url_for('scan', host=host, ports=ports))
    ## Render the index page template with the form
    return render_template('index.html', form=form)

Эта часть скрипта app.py определяет маршрут для главной страницы веб-приложения. Функция index() отображает шаблон index.html вместе с экземпляром ScanForm.

Когда форма отправляется и проходит проверки валидации, функция перенаправляет пользователя на маршрут сканирования, передавая данные формы (хост и диапазон портов) через параметры URL. Эта переадресация запускает процесс сканирования.

✨ Проверить решение и практиковаться

Реализация маршрута сканирования

В этом шаге необходимо создать маршрут для выполнения фактического сканирования и отображения результатов. Добавьте следующую функцию в app.py:

## Define route for the scan results
@app.route('/scan')
def scan():
    ## Retrieve host and ports from the query string
    host = request.args.get('host')
    ports = request.args.get('ports')
    ## Perform the scan using Nmap
    nm.scan(hosts=host, ports=ports, arguments='-sV')  ## -sV for service/version detection
    scan_results = []

    ## Process scan results and store them in a list
    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', '')
                })

    ## Render the results page template with the scan results
    return render_template('results.html', scan_results=scan_results, host=host)

Функция scan() обрабатывает маршрут, отвечающий за выполнение фактического сканирования портов и отображение результатов. Она извлекает хост и диапазон портов из параметров строки запроса, переданных в URL.

С использованием экземпляра Nmap PortScanner она проводит сканирование на указанном хосте и портах с аргументом -sV для определения версий сервисов.

Результаты сканирования обрабатываются и упорядочиваются в список словарей, каждый из которых содержит сведения о сканируемом порте. Эти сведения затем передаются в шаблон results.html, где они отображаются для пользователя.

✨ Проверить решение и практиковаться

Запуск Flask-приложения

Создав все компоненты, вы готовы запустить Flask-приложение и запустить сканер TCP-портов. Последний код, необходимый в вашем app.py, гарантирует, что Flask-приложение будет запускаться только при прямом выполнении скрипта, а не при импорте его в качестве модуля в другом скрипте. Это распространенная практика в Python-приложениях, которые включают исполняемый скрипт.

Вставьте следующий фрагмент кода в конец файла app.py:

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

Этот код instructs Flask to start your application with debugging enabled, making it easier to track down errors. The application will listen on all network interfaces (host='0.0.0.0') and use port 8080. Debug mode should only be used during development, as it can be insecure to use in a production environment.

To run your Flask application, make sure you're in the project directory where app.py is located. Then, execute the following command in terminal:

python app.py

Switch to Web 8080 tab to access the TCP port scanner. You can now enter a host and a port range to scan, and view the results on the results page.

Порты 22 и 3306 обычно связаны с SSH и MySQL-сервисами соответственно, а порт 3000 используется для WebIDE-окружений.

✨ Проверить решение и практиковаться

Резюме

В этом проекте вы узнали, как создать простой, но мощный веб-сканер TCP-портов с использованием Flask и Nmap. Мы начали с настройки проекта и установки необходимых зависимостей. Затем мы создали Flask-приложение, обрабатывали отправку форм, выполняли сканирование портов и отображали результаты в удобочитаемом виде. Этот проект является прекрасным введением в веб-разработку с использованием Flask и сканирование сети с использованием Nmap, предлагая практическое применение, которое сочетает оба навыка.