HTML-include (локальный импорт) или как собрать один html из кучи маленьких

Крохотный perl-скрипт для облегчения одной нудной задачи.

Подсмотрено тут https://www.linux.org.ru/forum/talks/11731305#comment-11731973

r-asian@localhost:~/Music/dk$ tree
.
├── build.pl
├── index.html
└── pages
    ├── 1.html
    └── 2.html

Continue reading


Скрипт определения какие сайты из списка отдаются с определённого сервера.

Задача такая. Есть файл domains.txt, куда выведен выхлоп команды

ls -1 /etc/apache2/sites-enabled/

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

Пишем небольшой скрипт domain_checker.pl

#!/usr/bin/perl
 
my @lines = ();
my $ip = $ARGV[0];
while(<STDIN>){
    chomp;
    @lines = `host $_`;
    chomp @lines;
    print $_."\n" if $lines[0]=~m/$_\s+has\s+address\s+$ip/i;
}

И пускаем его в таким образом

cat domains.txt | ./domain_checker.pl server_ip

А он нам выводит список доменов, отдающихся с этого сервера.

 


Скрипт, автоматизирующий создание виртуально хоста apache, сборку PHP как CGI+suexec и создание файла зоны DNS.

В статье установка нескольких версий PHP на один сервер (с помощью CGI и suexec) рассмотрена сборка и установка PHP в качестве CGI+suexec. Настало время объединить всё это в один скрипт, который

  1. Создаст нужные папки и симлинки
  2. Соберёт PHP из исходников
  3. Настроит виртуальный хост Apache, для работы с этим доменом
  4. Создаст файл зоны DNS для Bind

Админу остаётся только

  1. Обработать скрипт напильником для работы на своём сервере
  2. Создать пользователя, от которого будет запускаться php-скрипт.
  3. Запустить скрипт, исходники которого даны ниже в формате
    ./build_vh.sh {Имя домена} {Имя пользователя} {Алиас домена для тестирования}

 

Скачать исходник


Ежедневные, еженедельные и ежемесячные резервные копии(бэкапы) на FTP

Выкладываю написанный для конкретных целей perl-скрипт делающий резервные копии на FTP по следующему алгоритму

  1. В определённое время суток делаем архив указанных каталогов
  2. В определённый день недели копируем ежедневный архив, делая из него еженедельный
  3. В определённый день месяца копируем ежедневный архив, делая з него ежемесячный
  4. Сравниваем состав локального хранилища и хранилища на ФТП, по необходимости закачивая на FTP отсутствующие архивы

Continue reading


Информатор о состоянии баланса Мегафон

Информация о состоянии баланса будет получаться из web-интерфейса управления аккаунтом и писаться в файлик, чтобы оттуда без труда забирать приложением

Мегафон

#!/usr/bin/perl -w
use POSIX;

#============ Настройки скрипта ===============================
# URL по которому получаем ID сессии
my $url1 = "https://serviceguide.megafonvolga.ru/ps/scc/php/check.php";
# URL по которому получаем информацию об аккаунте
my $url2 = "https://serviceguide.megafonvolga.ru/SCWWW/ACCOUNT_INFO";
# Логин и пароль для входа в сервисгид
my $login = "927***";
my $password = "****";
# Путь до wget
my $wget_path = "/usr/bin/wget";
# Временный файл
my $balance_tmp_file = "/tmp/megafon.balance";
# Файл, в который пишется результат
my $balance_file = "/var/log/megafon.info";
# Файл лога
my $log_file = "/var/log/balance.log";

# Загружаем XML с id сессии
my $command = "$wget_path -o /dev/null -O $balance_tmp_file --post-data=\"LOGIN=$login&PASSWORD=$password\" \"$url1\"";
`$command`;
open (A,$balance_tmp_file);
my ($session_id) = <A>;
close(A);
chomp($session_id);
loger("Download megafon session_id XML ",$session_id);

# Парсим ID сессии
$session_id =~s|<SESSION_ID>(.*)</SESSION_ID>|$1|gi;
loger("Parsing megafon session_id XML ",$session_id=~/[\d\w\.]+/gi);

# Загружаем информацию об аккаунте, передав ID сессии
$command = "$wget_path -o /dev/null -O $balance_tmp_file --post-data=\"SESSION_ID=$session_id\" \"$url2\"";
`$command`;

# Получаем баланс и записываем в результат
open (A,$balance_tmp_file);
my $balance = join "", <A>;
close(A);
chomp($balance);
loger("Download megafon account info ",$balance);

# Парсим баланс
loger("Parsing megafon account info",$balance=~/balance.*?>.*?([\d\.]+).*?</gim);

`echo "balance:$1" > $balance_file`;

# Убираем за собой
unlink($balance_tmp_file);

sub loger{
    my ($string, $result) = @_;
    my $now_string = strftime "%a %b %e %H:%M:%S %Y", localtime;
    open(B,">>".$log_file);
    if($result){
        print B "[Ok] ".$now_string." $string\n";
    }
    else{
        print B "[FAILED] ".$now_string." $string\n";exit(0);
    }
    close(B);
}

Сей скрипт так же пишет логи о своей работе вида

[Ok] Срд Янв  5 17:50:02 2011 Download megafon session_id XML
[Ok] Срд Янв  5 17:50:02 2011 Parsing megafon session_id XML
[Ok] Срд Янв  5 17:50:04 2011 Download megafon account info
[Ok] Срд Янв  5 17:50:04 2011 Parsing megafon account info

Результат выполнения одного тестового задания на Perl

Задание #1 (Perl)

Текст задания

Создать регулярное выражение, вытаскивающее дату и время. А также
комментарий, если он есть. Результат вывести на экран в отсортированном по
дате (в убывающем порядке).
Должно получиться только одно регулярное выражение, разбирающее все 3
варианта.

Входные данные (data01.txt)

Вариант 1: ^92(28/10/2002)**&[19:10:50]@Comment: Text String
Вариант 2: ^-06/02/2002*$21:55:45
Вариант 3: ^*13/08/2002*DX145&[22:50:20]@Comment: Text String

Решение

#!/usr/bin/perl -w
use strict; # Строгий синтаксис

# Получаем имя файла со стоками для парсинга из 1-го параметра командной строки
my $filename=$ARGV[0] if defined $ARGV[0] && $ARGV[0];
# По умолчанию(если в командной строке не задали) - data01.txt
$filename = "data01.txt" if !$filename;

# Открываем файл для чтения, если не открылся - заканчиваем и пишем сообщение об
# ошибке
open(A,$filename) or die $!;
print "============ ============\n";

my @list = ();
while(<A>){
    # Помещаем результат парсинга каждой строки в массив в виде хэша
    push
        @list,
        {"year",$3,"mon",$2,"day",$1,"time",$4,"comment",$6}
        if
            ~m/^
            .*[^\d](\d+)\/(\d+)\/(\d+)[^\d] (?#Вытаскиваем дату)
            .*?[^\d](\d+:\d+:\d+)[^\d]      (?#Вытаскиваем время)
            .*?(Comment:(.*)){0,1}          (?#Вытаскиваем комментарий)
            $/ix;

}

# Выводим строки в порядке убывания даты
foreach my $item
(
    sort
    { # Начало: Задаём свою функцию сравнения элементов массива
        $b->{year}.$b->{mon}.$b->{day}.$b->{"time"}
        cmp # Сравниваем как строки
        $a->{year}.$a->{mon}.$a->{day}.$a->{"time"}
    } # Конец: Задаём свою функцию сравнения элементов массива
    @list
)
{
    # NO COMMENT, если комментария нет
    $item->{comment} = "NO COMMENT" if !$item->{comment};
    # Форматированный вывод результата
    printf("%s/%s/%s %s - %s\n",
        $item->{day},$item->{mon},$item->{year},$item->{"time"},$item->{comment}
    );
}

print "============= / ============\n";
close(A) or die $!;

Задание #2 (MySQL + Perl)

Текст задания

Требуется создать базу с древовидным строением, и вывести все это одним
запросом MySQL, в виде дерева, учитывая, что в каждом разделе, может быть
сколько угодно подразделов.

Решение

Дамп БД (data02.sql)

DROP TABLE IF EXISTS `testtask_tree`;

CREATE TABLE IF NOT EXISTS `testtask_tree` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID раздела',
  `parent_id` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'ID родительского раздела',
  `level` int(11) NOT NULL DEFAULT '0' COMMENT 'Уровень вложенности',
  `name` varchar(255) NOT NULL COMMENT 'Название раздела',
  PRIMARY KEY  (`id`),
  KEY `parent_id` (`parent_id`),
  KEY `level` (`level`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COMMENT='Разделы' AUTO_INCREMENT=12 ;

  INSERT INTO `testtask_tree` (`id`, `parent_id`, `level`, `name`) VALUES
  (1, 0, 0, 'Определения'),
  (2, 0, 0, 'Функции, использующие регулярные выражения'),
  (3, 0, 0, 'Как работают регулярные выражения'),
  (4, 1, 1, 'одиночные символы (characters)'),
  (5, 1, 1, 'классы символов (character classes)'),
  (6, 1, 1, 'квантификаторы (quantifiers)'),
  (7, 1, 1, 'мнимые символы (assertions)'),
  (8, 7, 2, '^ - начало строки текста'),
  (9, 7, 2, '$ - конец строки или позиция перед символом начала новой строки, распо-ложенного в конце'),
  (10, 2, 1, 'split'),
  (11, 2, 1, 'grep');

Perl-скрипт

#!/usr/bin/perl
use strict;
use DBI; # Подключаем модуль работы с БД

# Определяем параметры подключения к БД.
my $DB_HOST = "localhost";
my $DB_USER = "acheto";
my $DB_PASS = "zaqwedcxs";
my $DB_NAME = "acheto";
my $DB_DUMP = "data02.sql";

################### Необязательная инициализирующая часть задачи ###############
# Заливка дампа БД
my $command = "mysql -u $DB_USER -p$DB_PASS -h $DB_HOST ".
    "--default-character-set=utf8 $DB_NAME < $DB_DUMP";
`$command`;
################ Конец: Необязательная инициализирующая часть задачи ###########

# Соединяемся с СУБД
my $dba=DBI->connect("DBI:mysql:dbname=$DB_NAME:host=$DB_HOST",$DB_USER,$DB_PASS)
    or die "Cant connect to database\n";
# Выставляем кодировку соединения
$dba->do("SET NAMES 'utf8';");

my $query = "
    SELECT
        `id`,           -- ID элемента дерева
        `parent_id`,    -- ID родителя элемента дерева
        `level`,        -- Уровень вложенности элемента (этим полем мы
                        -- проигрываем в дисковом пространстве, и хлопотах по
                        -- формированию дерева, зато ускоряем процесс его вывода)
        `name`          -- Название элемента дерева
    FROM
        `testtask_tree`
";
# Отправляем запрос
my $stha = $dba->prepare($query) or die;
$stha -> execute or die;

# Вытаскиваем элементы дерева по одному, вставляя их в массив сразу за
# их родителями (функция search_index_by_id позволяет нам определить индекс
# родительского элемента в массиве по его ID)
my @tree = ();my $index = 0;
while(my @row = $stha->fetchrow_array){
    splice @tree, search_index_by_id($row[1], \@tree)+1, 0, \@row;
}

# Окончательный вывод дерева
print "=================================\n";
foreach my $row(@tree){
    print " " x 4 x @{$row}[2];
    print "* ";
    print @{$row}[3]."\n";
}
print "=============================\n";

# Отсоединяемся от СУБД
$dba->disconnect;

# Функция для нахождения индекса в массиве по ID элемента
sub search_index_by_id{
    my ($id, $array) = @_;
    return 0 if $id == 0;
    for(my $i=0;$i<scalar @{$array};$i++){
        return $i if @{@{$array}[$i]}[0] == $id;
    }
    return -1;
}

Catalyst. Perl web-фрэймворк. Руководство по эксплуатации. Основные принципы.

Gerda Shank, gerda.shank@gmail.com Kennedy Clark, hkclark@gmail.com

Основы Catalyst

Описание

В этой части руководства мы создадим очень простое web-приложение на основе Catalyst, демонстрирующее такие мощные инструменты как:

  • Вспомогательные скрипты, которые могут быть использованы для быстрого создания и настройки структуры приложения
  • MVC (Model/View/Controller), реализующий такую структуру, которая способствует качественному разделению функций между различными частями приложения. Существует множество документации, раскрывающей это определение в деталях, поэтому MVC не будем обсуждать его здесь подробно. В кратце:
    • Модель (Model) обычно отражает структуру данных. В большинстве приложений модель приравнивается к объектам, создаваемым и сохраняемым в вашей базе данных в виде SQL.
    • Вид (View) отображает объекты модели в удобной для пользователя форме. Обычно он создаёт html страницы для браузера на основе шаблонизаторов, но также может быть представлен в других формах, таких как PDF-документ или Excel-таблица
    • Контроллер (Controller), как видно из названия, контроллер позволяет пользователю получать и вызывать и обрабатывать необходимые представления и модели.
  • ORM — технология представления объектов-связей (Object-Relational Mapping) используется для доступа к БД. Фактически, ORM реализует стандартизированные и автоматизированные возможности по созданию и сохранению объектов в реляционной БД.

Инструкции о том, как можно получить исходный код с примерами из subversion репозитария catalyst можно найти в разделе «введение»

Создание проекта в Catalyst

В Catalyst есть несколько вспомогательных скриптов, которые позволяют быстро создать основную структуру вашего приложения. Все приложения в Catalyst создаются запуском вспомогательного скрипта catalyst.pl, для него, начиная с версии 5.7000, необходимо установить Catalyst::Runtime и Catalyst::Devel.

В этой части руководства скрипт catalyst.pl будет использован для иницилизации фрэймворка для приложения, называемого hello

  $ catalyst.pl Hello
  created "Hello"
  created "Hello/script"
  created "Hello/lib"
  created "Hello/root"
  ...
  created "Hello/script/hello_create.pl"
  $ cd Hello

Далее показана структура каталогов, которую создаст вспомогателоьный скрипт catalyst.pl.

Changes # Запись об изменениях в приложении

  lib                   # Каталог для Perl-модулей
      Hello             # Каталог для кода приложений
          Controller    # Каталог для модулей контроллеров
          Model         # Каталог для моделей
          View          # Каталог для видов
      Hello.pm          # Основной модуль приложения
  Makefile.PL           # Makefile для сборки приложения
  hello.conf            # Файл конфигурации приложения
  README                # README file
  root                  # Подобный htdocs, каталог для шаблонов css, и javascript
      favicon.ico
      static            # Каталог для статичных файлов
          images        # Каталог для изображений, используемых в окне приветствия
  script                # Каталог для Perl-скриптов
      hello_cgi.pl      # Запустить приложение как CGI (не рекомендуется)
      hello_create.pl   # Создать модели, виды, контроллеры
      hello_fastcgi.pl  # Запустить приложение как fastcgi
      hello_server.pl   # Обычный сервер разработки
      hello_test.pl     # Тестирование приложения из командной строки
  t                     # Каталог для тестов
      01app.t           # Заготовки тестов
      02pod.t           
      03podcoverage.t

Catalyst будет автоматически создавать модули в каталогах ControllerModel, и View. Скрипт hello_create.pl позволит сделать в них заготовки Perl-модулей, плюс тестовые файлы в каталоге

t. По умолчанию шаблоны располагаются в каталоге root. Скрипты в каталоге script и их имена всегда нычинаются с названия вашего приложения в нижнем регистре. Если оно называется MaiTai, то созданные скрипты будут иметь вид maitai_create.pl

И хотя радоваться ещё пока рано, но у нас уже есть функционирующее приложение. Мы можем использовать скрипт, который нам предоставляет Catalyst, для запуска сервера разработки и просмотреть с помощью браузера страницу по умолчанию. Все скрипты в каталоге script могут быть запущены из основного каталога вашего приложения Hello.

Наберите следующую команду для запуска встроенного веб-сервера

$ script/hello_server.pl
[debug] Debug messages enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
| Catalyst::Plugin::ConfigLoader  0.17                                       |
| Catalyst::Plugin::Static::Simple  0.20                                     |
'----------------------------------------------------------------------------'

[debug] Loaded dispatcher "Catalyst::Dispatcher"
[debug] Loaded engine "Catalyst::Engine::HTTP"
[debug] Found home "/home/me/Hello"
[debug] Loaded Config "/home/me/Hello/hello.conf"
[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class                                                           | Type     |
+-----------------------------------------------------------------+----------+
| Hello::Controller::Root                                         | instance |
'-----------------------------------------------------------------+----------'

[debug] Loaded Private actions:
.----------------------+--------------------------------------+--------------.
| Private              | Class                                | Method       |
+----------------------+--------------------------------------+--------------+
| /default             | Hello::Controller::Root              | default      |
| /end                 | Hello::Controller::Root              | end          |
'----------------------+--------------------------------------+--------------'

[info] Hello powered by Catalyst 5.7011
You can connect to your server at http://localhost:3000

Проследуйте в браузере по адресу http://localhost:3000 (подставьте имя нужного хоста) и будете поприветствованы страницей-приглашением Catalyst. В отладочном выводе сервера разработки появится информация, подобная этой:

[info] *** Request 1 (1.000/s) [10301] [Sun May 18 10:11:36 2008] ***
[debug] "GET" request for "/" from "127.0.0.1"
[info] Request took 0.017964s (55.667/s)
.----------------------------------------------------------------+-----------.
| Action                                                         | Time      |
+----------------------------------------------------------------+-----------+
| /default                                                       | 0.000540s |
| /end                                                           | 0.001246s |
'----------------------------------------------------------------+-----------'

Нажмите Ctrl+C, чтобы остановить сервер разработки.

Hello, world

Простейший путь

Контроллер Root.pm является местом, где описываются действия, выполняемые, как правило, из корневого URL. Откройте файл lib/Hello/Controller/Root.pm в любом текстовом редакторе. Вы увидите функцию default, которая отвечает за отображение страницы-приглашения, которую ранее видели в браузере. Позже, возможно будет желание сменить её на что-то более полезное, например сообщение о 404-й ошибке. Но пока оставьте её в таком виде.

sub default :Path :Args {
    my ( $self, $c ) = @_;

    $c->response->body( $c->welcome_message );
}

$c в данном контексте — ссылка для доступа к Catalyst-приложению. Дополнительно она предоставляет доступ к объектам response и request (см. Catalyst,Catalyst::Response, и Catalyst::Request).

$c→response→body определяет содержимое HTTP ответа, а

$c→welcome_message — специальный метод(обработчик), возвращающий сообщение-приглашение, которое вы уже видели в браузере.

:Path :Args после имени метода — это аттрибуты, определяющие какие URL будут обрабатываться этим методом(обработчиком)(в зависимости от вашей версии Catalyst может быть использован «Private», но лучше этого не делать).

Некоторые MVC фрейворки управляют обработкой URL из одного места. Политика Catalyst такова, что управление обработкой URL осуществляется обработчиками внутри контроллеров. Это даёт гибкость в определении соответствия URL и обработчика. Обработчик default будет вызываться для всех URL, потому что не определён путь (ничего не указано после Path), и будет принимать любое количество аргументов (ничего не указано после Args).

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

Например, URL /admin/articles/create обрабатывается Hello::Controller::Admin::Articles, и обработчиком create внутри него.

Добавьте следующий обработчик в libs/Hello/Controller/Root.pm:

sub hello : Global { my ( $self, $c ) = @_; $c→response→body(«Hello, World!»); }

В данном примере вы посылаете в браузер свою собственную строку.

Сохраните файл, перезапустите сервер разработки и проследуйте на http://localhost:3000/hello, чтобы увидеть «Hello, World!».

Hello, World! с использованием вида и шаблона

В понятиях Catalyst «ВИД» — это не не XHTML — страница или или шаблон для представления в браузере. Это модуль, определяющий тип вывода — HTML, pdf, XML.

Чтобы создать TT-вид, запустите

$ script/hello_create.pl view TT TT

Будет создан модуль lib/Hello/View/TT.pm, который является дочерним классом Catalyst::View::TT. Ключевое слово view говорит скрипту, что мы создаём вид. Первое TT, что что вид будет на основе ToolkitTemplate шаблонизатора, а второе TT, что имя у модуля вида будет TT.pm(он будет использован для всех видов TT, и вы можете назвать его как хотите, например HTML.pm). Если посмотрите внутрь него, то обнаружите только настройки в виде константы, определяющей, что расширение для TT — это .tt

Теперь, когда вид «TT.pm» существует, Catalyst автоматически обнаружит его и позволить использовать его для отображения шаблонов, используя метод

«process»

Template Toolkit — прекрасно документированный (http://template-tookit.org), мощный шаблонизатор, но поскольку это не документация по TT, мы рассмотрим лишь некоторые его основные возможности.

Создайте файл шаблона root/hello.tt (Разместите в подкаталоге root каталога приложения Hello). Вот простой пример:

  [% META title = 'Hello, World!' %]
  <p>
      This is a TT view template, located in the 'root/' directory.
  </p>

[% and %] — маркеры частей TT — шаблона. Внутри них вы можете получить доступ к переменным и классам Perl, а так же использовать директивы TT. За пределами маркеров шаблон представляет из себя самый обычный HTML. Замените обработчик hello в lib/Hello/Controller/Root.pm следующим:

  sub hello : Global {
      my ( $self, $c ) = @_;

      $c->stash->{template} = 'hello.tt';
  }

В этот раз, вмето того, чтобы использовать $c→response-body(), устанавите значения ключа template хэша stash. В этот хэш помещаются данные, которые должны быть видны в других частях приложения. Ключ template определяет какой шаблон должен использоваться для отображения в конце обработчика.У контроллеров Catalist для всех обработчиков есть действие по умолчанию «end», которое выполняется при формировании страницы если нет оператора $c→response→body(). Таким образом ваш шаблон будет отображен при окончании работы вашего обработчика.

Сохраните lib/Hello/Controller/Root.pm и перезапустите сервер разработки, и снова взгляните на http://localhost:3000/hello. Вы должны увидеть результат применения шаблона.

Создание простого контроллера и действия

Создайте контроллер с именем «Site» выполнив

$ script/hello_create.pl controller Site

Будет создан файл lib/Hello/Controller/Site.pm (и тестовый файл). Добавьте туда обработчик

sub test : Local {
    my ( $self, $c ) = @_;

    $c->stash->{username} = "John";
    $c->stash->{template} = 'site/test.tt';
}

Обратите внимание на аттрибут Local у этого обработчика. Это позволяет ему отрабатывать URL вида «контроллер/обработчик», в нашем случае «site/test» вместо корневого URL, как при «Global». Имя шаблона указывать необязательно, поскольку TT по умолчанию будет пытаться использовать шаблон следующего вида «контроллер/обработчик.tt», но возможны ситуации, когда вам необходимо определить своё (например ещё неизвестно имя обработчика, или если не соблюдается трансляция имён по умолчанию). Так же в stash вставим ключ «username» для использования в шаблоне.

Создайте подкаталог site в каталоге root. Скопируйте

hello.tt как root/site/test.tt. Включите туда строку

<p>Hello, [% username %]!</p>

Перезапустите сервер разработки и проследуйте по адресу http://localhost:3000/site/test. Вы должны увидеть интерпретированный файл test.tt, включая установленное контроллером имя John.


Псевдо-ftp-сервер(ftp-прокси). Лучший способ не выдать пароля — не знать пароля

Предыстория создания

В начале 2007 года в сложилась довольно неприятная ситуация. На продвигаемых сайтах завелся вирус.

После анализа ситуации стал более-менее понятен алгоритм его работы

  1. при заходе на зараженный сайт, содержащий невидимый фрэйм, браузером посылался http-запрос по адресу, указанному в этом фрейме. Страница ответа на запрос содержала вредоносный код, внедряющий через дыру в internetexplorer троянца.
  2. в процессе своей работы троянец регулярно посылает найденные ftp-пароли налево (тут наши мнения разделились — одни утверждали, что троянец мониторит трафик и крадёт передаваемые в незашифрованном виде пароли прямо из уходящих в сеть данных, другие считали, что он просто читает файлы популярных ftp-менеджеров, таких как fartotal commander и пр.)
  3. некий скрип, используя полученные от троянца пароли заражает сайт, через внедрение во всё, имя чего начинается на index. невидимого фрейма со ссылкой на вредоносную страницу.

Проблема решается в 3 этапа:

  1. удаление вредоносного кода со страниц сайта;
  2. смена аттрибутов ftp-доступа;
  3. чистка компьютера несколькими антивирусами, как показывает практика, ни один из существующих антивирусов не обеспечивает 100% результата.

Однако всё гораздо сложнее, если ftp-доступ имеют несколько пользователей с разных компьютеров под управлением ОС windows. После того как проблема была решена в краткосрочном периоде(чистка, смена паролей), была начата работа по выработке методик и разработке средств не позволяющих данной ситуации проявиться вновь.

Во весь рост встала проблема утечки паролей. На тот момент была распространена практика выдачи настоящих реквизитов доступа ftp всем кто работает с сайтом. В силу огромного количества поддерживаемых сайтов, большого разнообразия хостеров (а стало быть и панелей управления) достаточная частая смена паролей была невозможна. Было решено сократить число лиц, соприкосающихся с базой паролей вплоть до того, чтобы непосредственно работающие с сайтом люди их не знали.

Основная проблема, которая и привела к столь сложному решению — невозможность гибко настраивать FTP-серверы.

Механизм взаимодействия ftp-клиента и ftp-сервера

Прежде всего, хочу порекомендовать две довольно приличные статьи про FTP протокол Протокол пересылки файлов FTP Служба FTP. Протокол FTP. Протокол TFTP.

Как известно ftp-протокол использует 2 сокетных соединения, образующих 2 «канала» передачи — канал передачи команд и канал передачи данных.

Канал передачи команд

ftp-сервер, как правило использует 21-порт для приёма подключений клиентов. При подключении он начинает принимать команды от клиента и возвращать ответы. Авторизация так же производится через него.

Канал передачи данных

На какой из сторон будет приниматься подключение для образования этого канала клиент и сервер решают через канал передачи команд — договариваются о режиме передачи и о том какой ip/port для этого ипользовать. При пассивном режиме (есть ещё активный, но он не будет рассматриваться) серверный сокет открывается на ftp-сервере и принимает подключение клиента.

Авторизация

Практически весь процесс авторизации проходит через канал передачи команд.

Вот пример диалога сервера и клиента После подключения по 21-му порту

>>>220 proftpd 1.2.10 server (proftpd default installation) [127.0.0.1]
<<<user r-asian
>>>331 password required for r-asian.
<<<pass xxxx
>>>530 login incorrect.

Как видно, в данном случае авторизация не удалась по причине неверного пароля. А вот пример удачной авторизации:

>>220 proftpd 1.2.10 server (proftpd default installation) [127.0.0.1]
<<<user r-asian
>>>331 password required for r-asian.
<<<pass xxxx
>>>230 user r-asian logged in.
<<<syst
>>>215 unix type: l8
<<<type i
>>>200 type set to i
<<<pwd
>>>257 "/home/r-asian" is current directory.
<<<pasv
>>>227 entering passive mode (127,0,0,1,128,5).
<<<list -al
>>>150 opening ascii mode data connection for file list
>>>226 transfer complete.

Общая идея псевдо-ftp

ftp — клиент авторизуется, передав по каналу передачи данных имя пользователя и пароль. Теперь поставим посредника. При авторизации он принимает от ftp-клиента фальшивые имя пользователя и пароль, находит в своей БД соответствующие им настоящие реквизиты ftp и производит авторизацию на ftp-сервере с последующим открытием сеанса связи. Далее все команды клиента и ответы сервера ретранслируются.

Таким образом от клиента скрывается настоящие имя пользователя, пароль, хост и порт ftp-сервера, и вероятность их перехвата, как с помощью грубой физической силы(вплоть до терморектального криптоанализа), так и с помощью хитрого снифа на участке от ftp-клиента до dummy-ftp сводится к минимуму. Круг хостов, с которых dummy-ftp может принимать подключения можно ограничить, и возможность утечки пароля на участке от ftp-клиента до dummy-ftp становится совсем призрачной.

Что такое dummy-ftp

dummy-ftp — ftp-прокси, выполняющий авторизацию на удалённом ftp-сервере, заменяя фальшивый пароль, передаваемый ftp-клиентом на реальный и далее ретранслирующий команды и ответы.

На данный момент он представляет из себя скрипт, написанный на языке perl. Первоначально предполагалось на Perl написать некий прототип системы и после тестирования переписать на C. Однако в таком виде он успешно работает почти год (в немного модифицированном виде:там все логи хранятся в БД) и от затеи его облагородить мы почти окончательно отказались.