Чтение онлайн

ЖАНРЫ

Шрифт:

Такой подход чреват некоторыми опасностями. Например, известный скрипт formmail Мэтта Райта в последнее время фильтрует SSI следующим образом:

value =~ s/<!–(.|\n)*–>//g;

Это несомненный прогресс по сравнению с первыми версиями, просто пропускавшими SSI. Однако проблема в том, что Apache, по крайней мере, не требует присутствия закрывающих символов «->» для выполнения указанной директивы, поэтому злоумышленник может добиться своего, просто введя строку <!–#include virtual=”some.html”.

Казалось бы, можно справиться с проблемой, «выкусывая» <!– (value =~ s/<!–//g;), но и в этом случае остается обходной маневр: строка <<!–!–#include virtual=”some.html” в итоге преобразуется в <!–#include virtual=”some.html”.

Достаточно безопасной можно считать конструкцию while(s/<!–//g){}, хотя и у нее

есть свои минусы. Похожие проблемы возникают на серверах, использующих ASP, PHP и т. д., причем большинство свободно распространяемых скриптов их просто игнорирует. Будьте крайне осторожны, адаптируя готовый скрипт к своему серверу, если он (сервер) умеет чуть больше, чем просто возвращать html-документы.

Создание безопасных CGI-приложений

Рассмотрим основные рекомендации по созданию безопасных CGI-приложений на Perl, позволяющие справиться с вышеописанными проблемами.

При использовании sendmail избавиться от ошибки очень легко – достаточно применить ключ «-t», запрещающий использовать адрес, переданный в командной строке, и передать его в заголовке письма:

...

$mailprog=’| /usr/sbin/sendmail -t’;

open (MAIL,"$mailprog ");

print MAIL "To: $address\nFrom: $from\nSubject: Confirmation\n\n";

print MAIL "Your request was successfully received\n";

received\n";

close MAIL;

Если же никак не удается избавиться от необходимости передачи пользовательского ввода оболочке, остается фильтровать в нем все специальные символы. Этих символов довольно много: <>|&;`’\”*$?~^[]{}\n\r. Самое простое, что можно сделать, – удалить все спецсимволы из введенной строки с помощью конструкции примерно такого вида:

$metasymbols = «][<>\|&;`’\»*\$\?~\^{}\n\r"; $string =~ s/[$metasymbols\\]//g;

Помимо этого постарайтесь гарантировать соответствие ввода предусмотренному шаблону. Скажем, для того же почтового адреса этим шаблоном может быть name@domain1.domain2, что чаще всего делается на Perl следующим образом:

die «Wrong address» if ($address !~ /^\w[\w\-.]*\@[\w\-.]+$/);

Здесь в начале и в конце строки ожидается один или несколько символов «a» – «z», «A» – «Z», «0» – «9», «-», «.» и «@» внутри, причем «-» или «.» не могут быть первыми. Правда, это не слишком помогает против атак, подобных приведенной выше, достаточно завершить наш псевдоадрес чем-нибудь типа ;@somewhere.ru. Если же у вас нет желания фильтровать спецсимволы, можно использовать другой вариант вызова функций system и exec, позволяющий передать не один аргумент, а список аргументов. В этом случае Perl не передает список аргументов в оболочку, а рассматривает первый аргумент как подлежащую выполнению команду и остальные аргументы – как параметры этой команды. Причем обычная для оболочки интерпретация спецсимволов не производится:

вместо system «grep $pattern $files»; использовать system «grep», «$pattern», «$files»;.

Этим же свойством можно воспользоваться для безопасного перенаправленного ввода/вывода. При этом нам понадобится знание того факта, что при открытии с перенаправлением вывода команды «-» мы неявно вызываем fork, создавая тем самым копию нашего процесса. В такой ситуации функция open возвращает 0 для дочернего процесса и pid дочернего процесса для родительского, что позволяет применять оператор or:

open (MAIL, «|-») or exec $mailprog, $address;

#open в родительском процессе возвращает ненулевое значение, и нет

#необходимости выполнять правую сторону or. Дочерний же процесс

#выполняет exec, после чего завершается.

print MAIL "From: $from\nSubject: Confirmation\n\n";

print MAIL "Your request was successfully received\n";

close MAIL;

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

Perl, запущенный в так называемом зараженном режиме (tainted mode), позволяет снять часть этого гнета. Чтобы попасть в такой режим, достаточно указать параметр «-T».

После этого работа Perl приобретает несколько параноидальный характер. Все переменные, проинициализированные за пределами программы, считаются зараженными и не могут быть переданы в качестве параметров потенциально опасным функциям, таким как system, exec, eval, unlink, rename

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

Переменные, инициализированные вне программы, – это переменные, значения которых получены из параметров программы, со стандартного входа, из переменных среды. Причем эта «зараза» распространяется, если использовать зараженную переменную для инициализации другой переменной – та тоже станет зараженной. «Зараза» остается, даже если мы проверили переменную на отсутствие всех спецсимволов либо очистили ее от них.

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

Единственный способ «обеззаразить» переменную – воспользоваться регулярными выражениями и применить извлечение совпадающей подстроки при поиске по маске. Это не слишком удобно, зато торжествует принцип «все, что не разрешено, – запрещено». Заодно приобретете опыт использования регулярных выражений, что наверняка пригодится в будущем.

...

Регулярные выражения хорошо знакомы опытным пользователям UNIX, они применяются во многих UNIX-утилитах, таких как grep, awk, sed, в редакторах (vi, emacs), в некоторых командных оболочках и т. д. Дополнительную информацию об использовании регулярных выражений можно найти практически в любой книге по Perl (например: Рэндал Шварц и Том Кристиансен «Изучаем Perl» (Randal L. Schwartzh and Tom Christiansen. Learning Perl); «Programming Perl» by Larry Wall, Tom Christiansen & Randal Schwartz и «Mastering Regular Expressions» by Jeffrey Friedl).

$address =~ /(\w[\w\-.]*)\@([\w\-.]+)/; $cleanaddress = $1.’@’.$2;

Все, что сопоставится выражению в первых круглых скобках, будет занесено в переменную $1, во вторых – в $2, и т. д. Переменные $1 и $2 будут уже считаться обеззараженными, и мы можем смело конструировать из них наш искомый адрес. Да, эти переменные тоже были получены из пользовательских данных, но Perl считает, что раз их значение получено из регулярного выражения, значит, они прошли нашу проверку и можно о них не беспокоиться. Чтобы быть уверенными до конца, вставим в наш код проверку:

if($address =~ /(\w[\w\-.]*)\@([\w\-.]+)/)

{

$cleanaddress = $1.’@’.$2;

}

else

{

warn "Wrong address: $address"; #выдавая сообщение об ошибке на stderr

$cleanaddress = "";

}

Тем самым, правда, отсекаются вполне законные имена типа mama&papa@home.org. Менее строгая проверка вида address=~/(\S+)\@([\w.-]+)/ пропустит и метасимволы, сведя на нет все наши усилия по обеззараживанию. У вас может возникнуть желание обеззаразить переменную следующим образом:

$address =~ (.*); $cleanaddress = $1;

Что ж, вольному – воля. Вы только что отключили все проверки и взяли всю ответственность на себя.

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

1. $ENV{"PATH"} = ’/bin:/usr/bin:/usr/local/bin’;

2. $ENV{"PATH"} = ’’;

Ошибки в известных CGI-скриптах

Примеры некоторых таких скриптов уже приводились выше – это и печально известный phf, и formmail (кстати, все скрипты Мэтта Райта, которые можно найти нафильтруют SSI именно описанным выше способом). Перечислим еще несколько.

Старые версии (1.0–1.2) счетчика TextCounter, вставляющегося в страницу через CGI, получали адрес обсчитываемой страницы из переменной окружения DOCUMENT_URI, при этом не производилась проверка на метасимволы со всеми вытекающими последствиями.

Популярный счетчик wwwcount , написанный на С, содержал традиционную ошибку, связанную с копированием содержимого переменной окружения QUERY_STRING в буфер фиксированной длины. Передав ему специально сформированную строку, можно было выполнить на сервере любой код. Затронуты версии 1.0–2.3. Версия 2.3 также содержала ошибку, позволяющую просмотреть на сервере любой GIF-файл с помощью следующего запроса:

http://www.victim.com/cgi-bin/Count.cgi?display=image&image=../../../../../../path_to_gif/file.gif.

Поделиться с друзьями: