2765 字
14 分钟
应用层协议
2026-03-14

FTP#

FTP (File Transfer Protocol) 并不是简单的“下载/上传”指令,它是一套基于 “控制”与“数据”分离 的复杂状态机协议。本文将带你通过协议原理与抓包分析,揭开 FTP 的神秘面纱。

1. 核心差异:为什么 FTP 有两个连接?#

在 HTTP 中,所有的交互(请求资源、上传数据)都在同一个 TCP 连接中完成。而 FTP 的设计哲学是 “双通道模式”

  • 控制连接(Control Connection): 默认端口 21。这是协议的“指挥部”,用于发送 USER, PASS, LIST, RETR 等文本指令。它在整个会话期间始终保持。
  • 数据连接(Data Connection): 专门用于传输文件内容或目录列表。它的特点是 “随用随建,用完即弃”。每执行一次传输操作,就会开启一条全新的 TCP 连接,传完后立即关闭。 为什么这么设计? 这种设计源于 FTP 诞生的年代(1971年),当时的网络环境极其不稳定。将控制指令与大数据传输物理隔离,可以确保即使数据传输发生中断,也不会导致整个控制会话挂掉。

2. FTP 主动模式与被动模式#

  • 主动模式(PORT): 客户端告诉服务器:“我在某个端口等着,你主动连接我”。
    • 问题: 在现代防火墙和 NAT 环境下,服务器往往无法穿透客户端内网发起连接。
  • 被动模式(PASV/EPSV): 客户端告诉服务器:“你准备好一个端口,把地址告诉我,我去连你”。
    • 趋势: 这是目前主流的模式,它通过服务器向客户端开放数据端口,完美规避了 NAT 问题。 调试建议: 如果你发现 FTP 连接能连上,但 lsget 时卡住,大概率是防火墙阻断了被动模式的动态端口。

3. 动手实践:通过抓包“看穿”协议#

理论再多,不如一次抓包。使用 Python 的 pyftpdlib 配合 tshark,可以直观地看到协议细节:

# 运行
# python3 ftp_server.py
# ftp -n localhost 2121
# 抓包
# tshark -i lo -f "tcp port 2121 or tcp portrange 60000-60010" -w output.pcap
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
# 实例化虚拟用户系统
authorizer = DummyAuthorizer()
# 添加匿名用户,目录设为当前文件夹
authorizer.add_anonymous(".") # elr
authorizer.add_user("admin", "123456", ".", perm="elradfmwMT")
# 实例化 FTP 处理器
handler = FTPHandler
handler.authorizer = authorizer
# 启动服务器,使用监听 2121 端口 (避免 21 端口需要 root 权限)
address = ('127.0.0.1', 2121)
server = FTPServer(address, handler)
# 设置被动模式端口范围(方便抓包观察)
handler.passive_ports = range(60000, 60010)
print("FTP server starting on 127.0.0.1:2121...")
server.serve_forever()

4. 时代的反思:为什么我们现在常用 SFTP?#

FTP 诞生于网络互信的早期,它的安全性设计严重落后于时代:

  • 明文传输: 用户名、密码和文件内容完全暴露。
  • 防火墙穿透难: 动态端口范围在企业级防火墙中配置非常复杂。 演进方向:
  • FTPS: 为 FTP 套上 SSL/TLS 的外壳(SSL/TLS over FTP)。
  • SFTP: 注意!它不是 FTP,它是基于 SSH 协议的子系统。它只需要一个 TCP 端口(通常是22),不仅自带加密,还解决了防火墙穿透的所有麻烦。

SMTP#

在现代互联网中,电子邮件依然是通信的基石之一。当你按下“发送”键,看似瞬间完成的过程,背后其实运行着SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)

1. 核心架构:推模式(Push Protocol)#

理解 SMTP 的第一步,是明确它的定位。与 HTTP(用户主动请求资源,即“拉”)不同,SMTP是一个典型的“推”协议。

  1. 邮件用户代理(MUA, Mail User Agent):如 Outlook客户端、QQ邮箱网页版等。
    • 职责:提供邮件撰写、阅读和管理界面。它负责通过 SMTP 将邮件推送给发件方的邮件服务器,并通过 POP3IMAP 从邮件服务器拉取接收到的邮件。
  2. 邮件传输代理(MTA, Mail Transfer Agent):邮件服务器接收邮件,负责路由中转
    • 路由解析:解析收件人地址,通过 DNS 查询目标域的 MX(Mail Exchange)记录,锁定下一跳 MTA。
    • 队列管理:MTA 会将邮件置于本地队列(Queue)。
    • 中转/投递:通过 SMTP 协议将邮件传递给 下一个MTA。当邮件到达目标服务器时,交由内部的 MDA (Mail Delivery Agent, 邮件投递代理) 存入收件人的专属目录中,等待用户MUA来拉取。

2. SMTP 的协议哲学:有状态的会话流#

SMTP 运行于 TCP 之上(标准端口 25),本质是一种基于明文命令 有状态的会话协议。它在逻辑上分为三个阶段:

  1. 会话建立 (Handshake)
    • 这是“身份与能力”的协商阶段。当 TCP 三次握手完成后,服务器会先发送一个 220 Service Ready。紧接着客户端发送 HELOEHLO(Extended HELO),双方确认身份并协商能力(例如是否支持 SIZE大数据块、AUTH身份认证、STARTTLS加密传输等)。
  2. 邮件事务处理 (Transaction)
    • MAIL FROM: 声明发件人地址
    • RCPT TO: 声明收件人地址(可多次调用以实现群发)
    • DATA: 命令发出后,SMTP 进入“内容接收模式”。此时服务器不再解析指令,而是接收整段原始文本,直到遇到 \r\n.\r\n 结束
  3. 会话终止 (Quit)
    • QUIT 命令不仅是 TCP 层面的终结,更是向服务器发送确认信号,告知邮件已完整交付,服务器随后可执行后续的路由或入库操作。
Terminal window
# 利用轻量级的邮件测试服务器 MailHog 和 Python,我们可以直观地观察上述过程。
# 运行 MailHog(一个轻量级的 SMTP 测试服务器,提供 Web UI 查看邮件内容 端口8025)
docker run -d --name mailhog -p 1025:1025 -p 8025:8025 mailhog/mailhog
# 简易用户代理(MUA)
import smtplib
from email.message import EmailMessage
# 构造邮件
msg = EmailMessage()
msg["Subject"] = "Hello from Python smtplib.SMTP"
msg["From"] = "sender@mail.alinche.com"
msg["To"] = "receiver@mail.alinche.com"
msg.set_content("这是一封测试邮件,用来观察 Wireshark 抓包。")
# 发送邮件(连接到本地的 MailHog)
try:
# MailHog 默认运行在 1025 端口
with smtplib.SMTP("localhost", 1025) as s:
s.send_message(msg)
print("邮件发送成功!")
except Exception as e:
print(f"发送失败: {e}")
## 或者者直接使用 telnet 模拟 SMTP 会话
# telnet localhost 1025
# HELO mycomputer
# MAIL FROM:<sender@test.com>
# RCPT TO:<receiver@test.com>
# DATA
# Subject: Hello from telnet Test Email
#
# This is a test message.
# .
# QUIT

3. 接收的艺术:POP3 与 IMAP#

SMTP 把邮件推到了目标服务器的“邮箱”队列里,但用户怎么在手机或电脑上看到它呢?这时候就需要拉模式(Pull Protocol) 登场了。目前主流的拉取协议有两种:POP3IMAP

传统的“下载与删除”:POP3 (Post Office Protocol version 3)#

POP3 就像传统的邮递员,它的设计理念诞生于早期互联网时代,那时的服务器存储空间非常昂贵,且用户通常只有一台电脑。

  • 工作机制:单向下载。MUA连接到邮件服务器,将所有未读邮件下载到本地设备,并且(默认情况下)从服务器上删除这些邮件
  • 优点
    • 节省服务器存储空间。
    • 可以在没有网络的情况下阅读已下载的历史邮件。
  • 致命缺点多设备灾难。如果你在手机上用 POP3 下载了邮件,回到家打开电脑,电脑上将看不到这封邮件(因为它已经从服务器上删除了)。
  • 端口110(明文) / 995(SSL/TLS加密)

现代的“云端双向同步”:IMAP (Internet Message Access Protocol)#

为了解决多设备办公的问题,IMAP 应运而生。它是现代邮件客户端的默认首选。

  • 工作机制:双向同步。IMAP 并不把邮件“拿走”,而是作为一面镜子,让客户端直接映射和操作服务器上的邮件
  • 核心特性
    • 状态同步:你在手机上把邮件标记为“已读”,电脑端和网页端也会同步显示“已读”。
    • 文件夹管理:支持在服务器上创建、重命名或删除文件夹,这些操作在所有设备上同步。
    • 按需加载:客户端可以先只下载邮件的“头部(发件人、主题)”,只有当你点击查看时,才下载正文和庞大的附件,极大节省了移动端流量。
  • 缺点:高度依赖网络,且长年累月会占用大量的服务器存储空间。
  • 端口143(明文) / 993(SSL/TLS加密)

4. 突破限制的魔法:MIME 与 Base64 编码#

早期的 SMTP 协议有一个致命缺陷:它被设计为只支持 7-bit ASCII 字符。如果你直接把中文、图片或 PDF 作为 DATA 内容发送,SMTP 服务器会直接报错或导致乱码。

  • MIME (Multipurpose Internet Mail Extensions,多用途互联网邮件扩展)
    • 它不是一个独立的传输协议,而是一个内容格式标准
    • MIME 通过在邮件头部增加 Content-Type 字段,告诉接收方这是一段 HTML 代码(text/html)、一张图片(image/jpeg)还是一个带有附件的混合内容(multipart/mixed)。
  • Base64 编码
    • 由于 SMTP 通道只能走 ASCII 字符,Base64 充当了“翻译官”的角色
    • 它将包含中文的文本、图片的二进制数据,统统转换成由 A-Z, a-z, 0-9, +, / 组成的 64 个安全且可打印的 ASCII 字符。(空间膨胀约33.3%)
    • 例如,中文的“测试”二字经过 Base64 编码后变成了 5rWL6K+V,这样 SMTP 就能安全地将其装在信封里传输。接收方的 MUA 收到后,再 Base64 逆向解码恢复成中文或图片。
  • 现代邮件的附件 本质上是一段经过 Base64 编码的二进制数据,MIME 通过 Content-Disposition: attachment 告诉客户端这是一个附件,客户端再根据 Content-Type 的提示正确解码并展示。

5. 现代 SMTP 的安全防线#

如果你现在试图直接使用原始 SMTP 协议与大型邮件服务器(如 Gmail、QQ 邮箱)交互,你大概率会碰壁。因为早期的 SMTP 是“君子协议”,任何人都可以伪造 MAIL FROM。现代邮件系统已经演进出三道坚固的防线:

  1. STARTTLS(安全升级):通过 STARTTLS 命令,SMTP 可以在通信中途将原本的明文 TCP 连接升级为 TLS 加密连接,防止邮件内容(含 Base64 数据)在公网被抓包窃听。
  2. 身份认证(SMTP-AUTH):现代 MUA 提交邮件时,必须通过 AUTH LOGIN/PLAIN 验证账户密码,防止邮件服务器沦为垃圾邮件的开放中继站(Open Relay)。
  3. 身份验证体系(SPF/DKIM/DMARC):由于协议漏洞导致的发件人易伪造问题,现代 DNS 层面的 SPF 记录(验证发送者 IP)和基于非对称加密数字签名的 DKIM,成为了现代邮件系统的“防伪标识”。
应用层协议
https://blog.alinche.dpdns.org/posts/net/ftpsmtp/
作者
Oeasy1412
发布于
2026-03-14
许可协议
CC BY-NC-SA 4.0