FTP (File Transfer Protocol) — прикладной протокол передачи файлов поверх TCP, в котором команды и сами данные идут по двум разным соединениям. Действующая спецификация — RFC 959 (1985), первые версии протокола появились еще в 1971 году в сети ARPANET.
Содержание
- Мини-словарь: четыре термина, которые путают
- Как проходит сеанс FTP
- Активный и пассивный режим
- Почему обычный FTP небезопасен
- FTPS и SFTP: в чем разница
- Пример: FTPS-клиент на Python
- Что проверить на своем FTP-сервере
- Если не получилось: симптомы и причины
- Где FTP используется сегодня
- Выводы
- Где применяется / связь с практикой
- FAQ
Ниже разберем, как устроен сеанс FTP, чем активный режим отличается от пассивного, почему обычный FTP не защищает пароль и чем его заменяют. Пример на Python проверен на Python 3.14 с тестовым сервером pyftpdlib 2.2.0 (сентябрь 2026).
Мини-словарь: четыре термина, которые путают
- Канал управления — TCP-соединение, по которому клиент шлет команды (
USER,PASS,RETR), а сервер отвечает кодами (230,550). Стандартный порт сервера — 21. - Канал данных — отдельное TCP-соединение для содержимого файла или списка каталога. Открывается заново под каждую передачу. Порт 20 относится только к активному режиму, и то как типичное значение при управляющем порте 21, а не обязательное.
- FTPS — тот же FTP, но с TLS поверх обоих каналов (RFC 4217).
- SFTP — другой протокол, подсистема SSH. С FTP у него общее только назначение и похожие команды клиента.
Как проходит сеанс FTP
Клиент подключается к порту 21, получает приветствие 220, передает логин и пароль, выбирает режим и тип передачи, а затем на каждую операцию с файлом открывается отдельный канал данных. Вот минимальный полный пример клиента на стандартной библиотеке Python.
Только для тестового стенда. Обычный FTP передает пароль и файл открытым текстом и не защищает их от подмены в пути, поэтому такой код годится лишь для локальной проверки. Пароль в примере берется из переменной окружения FTP_PASSWORD, а не записан в код.
import os
from ftplib import FTP
with FTP() as ftp:
ftp.set_debuglevel(1) # печатать команды и ответы
ftp.connect("127.0.0.1", 2121, timeout=10)
ftp.login("demo", os.environ["FTP_PASSWORD"])
ftp.set_pasv(True) # пассивный режим (по умолчанию и так True)
print(ftp.nlst())
lines = []
ftp.retrlines("RETR report.csv", lines.append)
print(lines)
Тестовый сервер слушал нестандартный порт 2121 (порты ниже 1024 требуют прав root). Реальный вывод прогона (начало):
*resp* '220 pyftpdlib 2.2.0 ready.'
*cmd* 'USER demo'
*resp* '331 Username ok, send password.'
*cmd* 'PASS ******'
*resp* '230 Login successful.'
*cmd* 'TYPE A'
*resp* '200 Type set to: ASCII.'
*cmd* 'PASV'
*resp* '227 Entering passive mode (127,0,0,1,195,90).'
*cmd* 'NLST'
*resp* '150 File status okay. About to open data connection.'
*resp* '226 Transfer complete.'
['report.csv']
Коды ответа трехзначные: 2xx — успех, 3xx — нужна следующая команда (после USER сервер ждет PASS), 4xx — временная ошибка, 5xx — постоянная.
Ответ 227 содержит шесть чисел: первые четыре — IP, последние два — порт по формуле p1 * 256 + p2. Здесь 195 * 256 + 90 = 50010, то есть сервер ждет подключения на порту 50010.
Перед NLST сервер может ответить 150 или 125 (канал данных уже открыт) — это зависит от того, успел ли клиент подключиться к порту данных, оба кода означают нормальное начало передачи.
Звездочки в строке PASS ****** — только маскировка в отладочном выводе ftplib. По сети пароль уходит открытым текстом, об этом ниже.
Активный и пассивный режим
Главное различие — кто открывает канал данных. Порты в таблице — типичная конфигурация, а не перечень обязательных значений.
| Активный режим | Пассивный режим | |
|---|---|---|
| Команда клиента | PORT (или EPRT для IPv6) |
PASV (или EPSV) |
| Кто подключается | Сервер к клиенту | Клиент к серверу |
| Порт сервера для данных | Обычно TCP/20 при управляющем TCP/21; фактический исходящий порт определяют конфигурация сервера и реализация | Выбранный из диапазона, например 50000-50010 |
| Проблема | Входящее соединение к клиенту режут фаервол и NAT | На сервере нужно открыть диапазон портов и правильно указать внешний IP |
| Где уместен | Внутренние сети без NAT, старое оборудование | Почти все современные сценарии, клиенты по умолчанию |
Упрощенно: в активном режиме клиент говорит «подключись ко мне сюда», в пассивном — спрашивает «куда подключиться к тебе». Поэтому для клиентов за домашним роутером пассивный режим работает без настройки, а администратору сервера нужно разрешить в фаерволе порт 21 и весь пассивный диапазон.
Отдельная проблема активного режима — атака FTP bounce: команда PORT может указать адрес третьей машины, и сервер подключится к ней от своего имени. Современные серверы по умолчанию отклоняют PORT на чужой адрес, но на старых устройствах это стоит учитывать.
Почему обычный FTP небезопасен
FTP не шифрует ни один из каналов. Любой, кто видит трафик, получает логин, пароль и содержимое файлов. Целостность тоже не защищена: файл можно незаметно заменить по пути, и получатель будет доверять подмененной копии. Проверки подлинности сервера нет: клиент не может отличить настоящий сервер от подмененного.
Отсюда практическое правило: обычный FTP используют лишь там, где не нужны ни конфиденциальность, ни целостность передачи. Даже публичный файл с анонимного FTP без пароля может прийти подмененным, а изолированный сегмент сети не снимает вопрос сам по себе — у него своя модель угроз. Для чувствительных данных и там, где важно убедиться, что вы говорите с нужным сервером, выбирают SFTP либо FTPS с проверкой сертификата.
FTPS и SFTP: в чем разница
| FTPS | SFTP | |
|---|---|---|
| Основа | FTP + TLS | Подсистема SSH |
| Порты | 21 (явный TLS, команда AUTH TLS), 990 (неявный, устаревший вариант) + диапазон портов данных |
22, одно соединение |
| Каналы | Два, как у FTP | Один, команды и данные мультиплексированы |
| Проверка сервера | Сертификат X.509 | Ключ хоста SSH (known_hosts) |
| Вход клиента | Пароль, клиентский сертификат | Пароль, SSH-ключ |
| Через NAT и фаервол | Сложнее: фаервол не видит зашифрованный ответ 227 |
Проще: один порт |
| Когда выбирать | Совместимость с FTP-инфраструктурой, требование партнера | Новые интеграции, администрирование серверов |
Частая путаница: SFTP — не «FTP через SSH-туннель» и не FTPS. Сервер FTPS не примет SFTP-клиента и наоборот.
Оба протокола закрывают три разные задачи, и их стоит различать. Конфиденциальность — посторонний не прочитает пароль и файл. Целостность — файл нельзя незаметно изменить в пути. Проверка сервера — клиент убеждается, что подключился к нужному серверу, а не к подмененному. Шифрование без проверки сервера дает только первые две: канал защищен, но неизвестно с кем. Поэтому в FTPS обязательна проверка сертификата, а в SFTP — сверка ключа хоста при первом подключении.
Пример: FTPS-клиент на Python
Рекомендуемый вариант для FTP-инфраструктуры — явный TLS с проверкой сертификата и шифрованием обоих каналов:
import os
import ssl
from ftplib import FTP_TLS
ctx = ssl.create_default_context(cafile="/tmp/ca.pem") # в проде - системные CA
with FTP_TLS(context=ctx) as ftp:
ftp.connect("localhost", 2121, timeout=10)
ftp.auth() # AUTH TLS: шифруем канал управления до логина
ftp.login("demo", os.environ["FTP_PASSWORD"])
ftp.prot_p() # PROT P: шифруем и канал данных
with open("report.csv", "wb") as f:
ftp.retrbinary("RETR report.csv", f.write)
print(open("report.csv").read())
Результат — содержимое файла, скачанное по зашифрованным каналам:
id,total
1,100
2,250
Здесь /tmp/ca.pem — самоподписанный сертификат тестового стенда. Для реального сервера файл CA не указывают: create_default_context() проверит сертификат по системному хранилищу и сверит имя хоста. Контекст обязательно передавать явно: FTP_TLS() без context= в Python (проверено на 3.14) создает непроверяющий контекст, и соединение установится даже с поддельным сервером — канал будет зашифрован, но неизвестно с кем. Отключать проверку (CERT_NONE, check_hostname = False) допустимо только на тестовом стенде; для самоподписанного сертификата правильнее указать его в cafile, как выше.
Типовая ошибка: забыть prot_p(). Без этой строки канал управления зашифрован, а файл пойдет открытым текстом. Сервер, настроенный требовать TLS на данных, откажет:
ftplib.error_perm: 550 SSL/TLS required on the data channel.
Исправление — вызвать ftp.prot_p() сразу после login(). Если же подключиться к такому серверу обычным FTP, отказ придет уже на логине: 550 SSL/TLS required on the control channel. Коды и тексты взяты из pyftpdlib, другие серверы могут отвечать иначе (например, 530 или 534).
Пароль в обоих примерах читается из переменной окружения FTP_PASSWORD; в рабочей среде его обычно хранят в хранилище секретов. Не записывайте пароль прямо в код скрипта.
Что проверить на своем FTP-сервере
Конкретные параметры зависят от сервера (vsftpd, ProFTPD, Pure-FTPd, IIS FTP), но требования общие:
- Отключить анонимный вход, если он не нужен явно.
- Требовать TLS для логина и данных, отклонять обычный FTP.
- Ограничить пользователя его каталогом (chroot) и дать минимальные права: только чтение, если запись не нужна.
- Задать узкий диапазон пассивных портов и открыть в фаерволе только его и порт управления.
- Указать внешний IP для ответа
227, если сервер за NAT. - Включить журнал входов и передач, ограничить число неудачных попыток входа.
- Проверить результат с внешней машины: обычный FTP-клиент должен получить отказ, FTPS-клиент — скачать файл.
Если не получилось: симптомы и причины
| Симптом | Вероятная причина | Что делать |
|---|---|---|
Логин прошел, LIST или загрузка зависают |
Закрыт пассивный диапазон или активный режим за NAT | Открыть диапазон, включить пассивный режим |
В ответе 227 внутренний IP (10.x, 192.168.x) |
Сервер за NAT не знает внешнего адреса | Указать внешний IP в настройках сервера |
425 Can't open data connection |
Канал данных не установился | Проверить фаервол и режим |
530 Login incorrect |
Неверные учетные данные или вход запрещен политикой | Проверить пользователя и требование TLS |
550 на файле |
Нет файла или прав | Проверить путь относительно chroot и права |
FTPS: соединение рвется после PASV |
Фаервол с FTP-инспекцией не видит зашифрованный ответ | Открыть пассивный диапазон явно |
Где FTP используется сегодня
Для обычного пользователя FTP почти исчез: Chrome и Firefox убрали поддержку ftp:// в 2021 году, файлы передают через облачные хранилища и HTTPS. Протокол остается в инфраструктуре: выгрузка сайтов на старый хостинг, камеры и МФУ, сохраняющие файлы на FTP-сервер, исторические интеграции с партнерами.
Из клиентов чаще встречаются FileZilla и WinSCP (графические), lftp и curl (командная строка), для SFTP — встроенный в OpenSSH клиент sftp. В новых проектах вместо FTP обычно берут SFTP, HTTPS API или объектное хранилище S3.
Выводы
- FTP передает команды по каналу управления (порт 21), а файлы — по отдельному каналу данных, который открывается на каждую передачу.
- В активном режиме канал данных открывает сервер (обычно с порта 20), в пассивном — клиент к порту из диапазона сервера; пассивный режим лучше работает через NAT.
- Обычный FTP не шифрует пароль и данные, не защищает файл от подмены и не проверяет подлинность сервера.
- FTPS — это FTP с TLS и двумя каналами, SFTP — отдельный протокол поверх SSH с одним портом 22.
- В FTPS нужно шифровать оба канала и проверять сертификат сервера: в Python это
FTP_TLS(context=ssl.create_default_context()),auth()иprot_p().
Где применяется / связь с практикой
Освойте тему на практике
С FTP и его защищенными вариантами сталкиваются сетевые инженеры и администраторы: открыть пассивный диапазон на фаерволе, разобраться с NAT, найти по дампу трафика, почему канал данных не поднимается. Эти навыки опираются на понимание TCP, портов и трансляции адресов. Если хотите системно освоить устройство сетей, посмотрите программу курса «Сетевой инженер. Basic». Разобрать отдельные темы с преподавателями можно на открытых уроках Otus.
FAQ
Можно ли открыть FTP-сервер в браузере?
В актуальных Chrome и Firefox нет: поддержку ftp:// из них удалили. Нужен FTP-клиент, файловый менеджер с поддержкой FTP или curl.
Что такое анонимный FTP?
Вход с логином anonymous без настоящего пароля. Так раньше раздавали публичные архивы; для приватных данных такой доступ отключают. Анонимный вход не защищает файл от подмены в пути, поэтому скачанное стоит сверять с контрольной суммой или подписью, полученной по защищенному каналу.
Чем отличаются режимы ASCII и binary?
В режиме ASCII (TYPE A) сервер и клиент могут менять концы строк под свою систему, в binary (TYPE I) файл передается байт в байт. Архивы, картинки и исполняемые файлы передают только в binary.



