Время прочтения: 5 мин.

Друзья, хочу поделиться с вами опытом решения одной из задач, поставленной передо мной в процессе аудита. Мне требовалось подтвердить или опровергнуть предположение о нарушении технологии обработки данных в автоматизированной системе, а именно: проверить содержимое каталогов, размещенных на FTP-сервере, на наличие в них определенных файлов.

Для решения данной задачи я использовал Python и его стандартный модуль ftplib.

 Опишу, как я это сделал. Для того, чтобы у вас была возможность попробовать самостоятельно проверить работу кода, я опишу процесс на примере FTP-сервера Ростовского госуниверситета (ftp.rsu.ru).

Итак, начнем. Импортируем необходимые модули:

import ftplib
import os
from datetime import datetime

Для входа на FTP-сервер потребуется указать учетные данные (имя пользователя и пароль), а  в случае возможности входить анонимно (это наш случай в приводимом для примера FTP-сервере) используем следующие строки:

FTP_HOST = 'ftp.rsu.ur'
FTP_USER = 'anonymous'
FTP_PASS = ''

Далее используем вспомогательные функции, которые позволят вывести список «найденных» каталогов и файлов:

# некоторые служебные функции, которые нам понадобятся
def get_size_format(n, suffix='B'):
    # преобразует байты в масштабированный формат (KB, MB, и т.д.)
    for unit in ['', 'K', 'M', 'G', 'T', 'P']:
        if n < 1024:
            return f'{n:.2f}{unit}{suffix}'
        n /= 1024

def get_datetime_format(date_time):
    # преобразовать в объект даты и времени
    date_time = datetime.strptime(date_time, '%Y%m%d%H%M%S')
    # преобразовать в удобочитаемую строку даты и времени
    return date_time.strftime('%Y/%m/%d %H:%M:%S')

Затем подключаемся к серверу, используя класс клиента FTP:

# инициализировать сеанс FTP
ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)

Для исключения проблем при работе с нелатинскими символами, изменим кодировку на UTF-8 :

# принудительное кодирование UTF-8
ftp.encoding = 'utf-8'

Теперь, когда мы получили доступ к  серверу, выведем приветственное сообщение, которое отправляется сервером после подключения (для уверенности, что мы оказались там, где надо 🙂

# вывести приветственное сообщение
print(ftp.getwelcome())

Вот оно!

220 ProFTPD 1.3.5b Server (FTP Server of Rostov State University — only anonymous logins are allowed) [195.208.245.253]

После подключения к серверу мы находимся в корневом каталоге. Посмотрим на его содержимое, используя команду MLSD, с помощью которой получаем список каталогов и файлов с их метаданными: разрешение, размер, дата последней модификации и т. д.

print('*'*50, 'MLSD', '*'*50)
# используем команду MLSD
print('{:30} {:19} {:6} {:5} {:4} {:4} {:4} {}'.format('File Name', 'Last Modified', 'Size',
                                                    'Perm', 'Type', 'GRP', 'MODE', 'OWNER'))
for file_data in ftp.mlsd():
    # извлечь возвращаемые данные
    file_name, meta = file_data
    # т.е. каталог, файл или ссылка и т. д.
    file_type = meta.get('type')
    if file_type == 'file':
        # если это файл, изменить тип передаваемых данных на IMAGE / binary
        ftp.voidcmd('TYPE I')
        # получить размер файла в байтах
        file_size = ftp.size(file_name)
        # преобразовать его в удобочитаемый формат (т. е. в «КБ», «МБ» и т. д.)
        file_size = get_size_format(file_size)
    else:
        # не файл, то может быть каталог
        file_size = 'N/A'
    # дата последней модификации файла
    last_modified = get_datetime_format(meta.get('modify'))
    # права доступа к файлу
    permission = meta.get('perm')
    
    # получить id файла
    unique_id = meta.get('unique')
    # группа пользователей
    unix_group = meta.get('unix.group')
    # файловый режим, разрешения Unix
    unix_mode = meta.get('unix.mode')
    # владелец файла
    unix_owner = meta.get('unix.owner')
    # вывод результата
    print(f'{file_name:30} {last_modified} {file_size:7} {permission:5} {file_type:4} {unix_group:4} {unix_mode:4} {unix_owner}')

Вот результат работы команды MLSD:

************************************************** MLSD **************************************************
File Name                      Last Modified       Size   Perm  Type GRP  MODE OWNER
.                              2020/02/14 11:51:40 N/A     fle   cdir 35901 0644 0
..                             2020/02/14 11:51:40 N/A     fle   pdir 35901 0644 0
docsvisionclient               2016/08/01 13:36:22 N/A     fle   dir  35901 0644 37411
terminal                       2011/09/24 18:22:19 N/A     fle   dir  35901 0644 0
Edu_tools                      2011/09/24 18:23:15 N/A     fle   dir  35901 0644 0
Solaris                        2020/03/31 21:15:57 N/A     fle   dir  35901 0644 0
net                            2012/01/27 14:16:46 N/A     fle   dir  35901 0644 0
fujitsu                        2017/09/11 14:32:06 N/A     fle   dir  35901 0644 0
security                       2012/01/27 14:22:14 N/A     fle   dir  35901 0644 0
hardware                       2011/09/24 18:15:29 N/A     fle   dir  35901 0644 0
development                    2012/01/27 14:02:03 N/A     fle   dir  35901 0644 0
archivers                      2011/09/24 18:38:42 N/A     fle   dir  35901 0644 0
databases                      2012/01/27 13:59:13 N/A     fle   dir  35901 0644 0
telephony                      2011/09/24 18:26:25 N/A     fle   dir  35901 0644 0
desktop                        2013/12/05 18:12:14 N/A     fle   dir  35901 0644 0
video                          2014/03/12 08:32:36 N/A     fle   dir  25512 0644 35422
media                          2011/09/24 18:19:13 N/A     fle   dir  35901 0644 0
publishing                     2012/01/27 14:20:54 N/A     fle   dir  35901 0644 0
shells                         2011/09/24 18:19:33 N/A     fle   dir  35901 0644 0
linux                          2018/04/20 05:22:10 N/A     fle   dir  35901 0644 0
local                          2011/09/24 18:04:29 N/A     fle   dir  35901 0644 0
bacula                         2015/09/21 08:00:37 N/A     fle   dir  35901 0644 35428
ccm                            2011/09/24 18:21:47 N/A     fle   dir  35901 0644 0
patches                        2014/08/11 18:26:39 N/A     fle   dir  35901 0644 0
gparted                        2014/09/16 12:06:29 N/A     fle   dir  35901 0644 0
public                         2013/06/07 13:11:38 N/A     adfr  OS.unix=symlink 35901 0644 0
office                         2012/01/27 14:17:31 N/A     fle   dir  35901 0644 0
text                           2011/09/24 18:19:33 N/A     fle   dir  35901 0644 0
sysutils                       2011/09/24 18:22:36 N/A     fle   dir  35901 0644 0
FreeBSD                        2020/02/14 12:07:50 N/A     fle   dir  0    0644 0

Далее, если потребуется сменить текущий каталог на какой-либо другой, например pub/maps, чтобы проверить его содержимое, используем команду cwd:

# изменить текущий рабочий каталог на каталог «pub» и подкаталог «maps»
ftp.cwd("linux/debian")

Если все ОК, получим сообщение:

‘250 CWD command successful’

Теперь можно повторить выполнение MLSD и вот содержимое ее работы

************************************************** MLSD **************************************************
File Name                      Last Modified       Size   Perm  Type GRP  MODE OWNER
.                              2020/05/13 10:51:46 N/A     fle   cdir 35901 0644 0
..                             2018/04/20 05:22:10 N/A     fle   pdir 35901 0644 0
firmware-10.4.0-amd64-DVD-1.iso 2020/05/09 12:00:00 3.69GB  adfr  file 0    0644 0
firmware-9.3.0-amd64-DVD-1.iso 2017/12/09 13:09:55 3.39GB  adfr  file 35901 0644 0
debian-7.6.0-amd64-CD-1.iso    2014/09/09 07:48:00 635.00MB adfr  file 35901 0644 44185
ql.iso                         2015/06/11 08:35:21 602.00KB adfr  file 1000 0644 1000
firmware-qlogic_20190717-2_all.iso 2020/05/13 10:51:46 3.38MB  adfr  file 0    0644 0
debian-8.4.0-amd64-CD-1.iso    2016/04/02 16:06:34 630.00MB adfr  file 35901 0644 0
firmware.iso                   2020/05/13 10:34:11 4.22MB  adfr  file 35901 0644 39622

После окончания работы с FTP-сервером закрываем соединение:

# выйти и закрыть соединение
ftp.quit()
В результате: '221 Goodbye.'

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