构建一个基于Web的TCP端口扫描器

HTMLHTMLBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在上一个项目中,我们开发了一个Python端口扫描器,它利用线程和套接字来扫描TCP端口。虽然它很有效,但使用第三方包仍有改进的空间。

在这个项目中,我们将通过集成 python-nmap 库来增强我们的端口扫描器,提供更强大的扫描功能。此外,我们将使用Flask构建一个Web应用程序,为我们的扫描器提供一个用户友好的界面。这个循序渐进的项目将指导你完成整个过程,确保你能够跟上并在现有知识的基础上进行构建。

👀 预览

🎯 任务

在这个项目中,你将学习:

  • 如何设置一个Flask项目并组织其结构
  • 如何使用Flask-WTF安全地创建和处理Web表单
  • 如何实现Flask路由来处理网页请求和提交
  • 如何在Python中使用Nmap库进行端口扫描
  • 如何使用Flask和HTML模板在网页上动态显示扫描结果
  • 如何应用基本的Tailwind CSS来增强前端设计

🏆 成果

完成这个项目后,你将能够:

  • 展示对使用Flask进行Web开发的基本理解,包括路由、模板渲染和表单处理
  • 应用将Python脚本与Web界面集成的实践经验
  • 展示在使用Nmap库进行网络扫描任务方面的熟练程度
  • 在Web应用程序中使用Flask-WTF进行表单创建和验证
  • 展示熟悉使用Tailwind CSS进行网页样式设计和增强用户界面设计
  • 创建一个基于Web的功能应用程序,它与后端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端口扫描器</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 = "正在扫描...";
        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端口扫描器</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是一个用于处理WTForms的Flask扩展,用于在模板中渲染这些字段。表单提交通过一个小的JavaScript函数 updateButton() 得到增强,该函数在表单提交时将提交按钮的文本更改为“正在扫描...”。这种视觉反馈告知用户他们的扫描请求正在处理中。

页面样式由Tailwind CSS处理,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>扫描结果</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">
          {{ 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">端口</th>
                <th class="px-4 py-3">状态</th>
                <th class="px-4 py-3">服务</th>
                <th class="px-4 py-3">版本</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应用程序

在这一步中,我们将为Flask应用程序创建主Python脚本。

app.py 中添加以下代码:

## 导入必要的模块
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

## 初始化Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'labex'

## 初始化Nmap端口扫描器
nm = nmap.PortScanner()

## 定义用于扫描输入的Flask-WTF表单
class ScanForm(FlaskForm):
    host = StringField('主机', validators=[DataRequired()])
    ports = StringField('端口范围', validators=[DataRequired(), Regexp(r'^\d+-\d+$', message="格式必须为起始值-结束值")])
    submit = SubmitField('扫描')

在这一步中,初始化了Flask应用程序和表单类。

app.py 脚本首先导入必要的模块,包括Flask本身、用于表单处理的Flask-WTF以及用于进行端口扫描的Nmap。创建了Flask应用程序实例并配置了一个密钥,以防止跨站请求伪造(CSRF)攻击。

ScanForm 类定义了用于主机和端口范围输入的表单字段,并使用验证器确保提供了数据且格式正确(特别是对于端口范围)。

'your_secret_key' 替换为用于保护表单免受CSRF攻击的真实密钥。

✨ 查看解决方案并练习

处理首页路由

在这一步中,我们将处理首页路由,用户可以在该页面提交他们想要扫描的主机和端口范围。在 app.py 中添加以下函数:

## 定义首页路由
@app.route('/', methods=['GET', 'POST'])
def index():
    form = ScanForm()  ## 实例化表单
    if form.validate_on_submit():
        ## 从表单中获取数据
        host = form.host.data
        ports = form.ports.data  ## 格式:"起始值-结束值"
        ## 重定向到扫描路由,并携带表单数据
        return redirect(url_for('scan', host=host, ports=ports))
    ## 使用表单渲染首页模板
    return render_template('index.html', form=form)

app.py 脚本的这一部分定义了Web应用程序首页的路由。index() 函数使用 ScanForm 实例渲染 index.html 模板。

当表单提交并通过验证检查时,该函数会将用户重定向到扫描路由,并通过URL参数传递表单数据(主机和端口范围)。这种重定向启动了扫描过程。

✨ 查看解决方案并练习

实现扫描路由

这一步涉及创建一个路由来执行实际的扫描并显示结果。在 app.py 中添加以下函数:

## 定义扫描结果路由
@app.route('/scan')
def scan():
    ## 从查询字符串中获取主机和端口
    host = request.args.get('host')
    ports = request.args.get('ports')
    ## 使用Nmap执行扫描
    nm.scan(hosts=host, ports=ports, arguments='-sV')  ## -sV用于服务/版本检测
    scan_results = []

    ## 处理扫描结果并将其存储在列表中
    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', '未知'),
                    'product': service.get('product', ''),
                   'version': service.get('version', ''),
                    'extra': service.get('extrainfo', '')
                })

    ## 使用扫描结果渲染结果页面模板
    return render_template('results.html', scan_results=scan_results, host=host)

scan() 函数处理负责执行实际端口扫描并显示结果的路由。它从URL中传递的查询字符串参数中获取主机和端口范围。

使用Nmap端口扫描器实例,它对指定的主机和端口进行扫描,并使用 -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')

这段代码告诉Flask在启用调试的情况下启动应用程序,这样更容易追踪错误。应用程序将监听所有网络接口(host='0.0.0.0')并使用端口 8080。调试模式仅应在开发期间使用,因为在生产环境中使用可能不安全。

要运行你的Flask应用程序,请确保你位于 app.py 所在的项目目录中。然后,在终端中执行以下命令:

python app.py

切换到Web 8080 标签页以访问TCP端口扫描器。现在你可以输入要扫描的主机和端口范围,并在结果页面上查看结果。

端口22和3306分别普遍与SSH和MySQL服务相关联,端口3000用于WebIDE环境。

✨ 查看解决方案并练习

总结

在这个项目中,你学习了如何使用Flask和Nmap构建一个简单而强大的基于Web的TCP端口扫描器。我们首先设置了项目环境并安装了必要的依赖项。然后,我们逐步创建了Flask应用程序,处理表单提交,执行端口扫描,并以用户友好的方式显示结果。这个项目是使用Flask进行Web开发和使用Nmap进行网络扫描的绝佳入门项目,提供了一个结合这两种技能的实际应用。

您可能感兴趣的其他 HTML 教程