Ключевые слова:perl, cgi, auth, web, access, http, mod_rewrite, (найти похожие документы)
Date: Wed, 24 Jul 2002 20:24:37 +0600
From: Andrey Sapozhnikov <[email protected]>
Newsgroups: fido7.ru.perl
Subject: http authorization выдачей 401 из CGI-скрипта
> Хм. А как делается http authorization выдачей 401 из скрипта?
--------------------- 401.cgi ---------------------
#!/usr/bin/perl -w
use strict;
print "Status: 401\r\n",
"WWW-Authenticate: Basic realm=\"test\"\r\n",
"Content-type: text/plain\r\n\r\n",
"Authorization required!\r\n";
---------------------- eof ------------------------
P.S. Данный скрипт имеет исключительно демонстрационную
цель и никоим образом не является призывом писать CGI
скрипты без использования библиотеки CGI. В реальной
работе используйте, если не имеете веских оснований
делать по-другому:
#!/usr/bin/perl -w
use strict;
use CGI;
my $q = CGI->new;
print $q->header(-status => 401,
-www_authenticate => 'Basic realm="test"',
-type => 'text/html'),
$q->start_html('Authorization required'),
$q->h2('Authorization required'),
$q->end_html;
P.P.S. Выдать ответ 401 еще не все. Hадо как-то получить
от браузера имя и пароль пользователя. Тут все зависит от
веб-сервера, но апач, к примеру, эти поля вам не передаст.
Выручить может mod_rewrite.
------------------ .htaccess -------------------
<Files "401.cgi">
RewriteEngine On
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
</Files>
--------------------- eof ----------------------
обеспечит передачу поля Authorization в переменной окружения
REMOTE_USER. Можете выбрать другую, но имейте в виду, что
CGI скрипты могут запускаться через suexec который содержит
жестко заданный список переменных окружения разрешенных к
передаче в скрипт. Содержимое этой переменной (например
'Basic bmFtZTpwYXNz') придется разобрать самостоятельно.
В общем ничего сложного. Метод авторизации, пробел, и строчка
имя:пароль завернутая при помощи base64.
From: Victor Wagner <[email protected]>
>> MAI> WWW-Authenticate: Basic realm="MuRealm"
>> MAI> HTTP/1.0 401 Unauthorized
.....
MAI> Я предполагал, что алгоритм такой.
MAI> - клиент лезет на скрипт
MAI> - действие, которое он хочет сделать, требует авторизации
MAI> - генерится ошибка 401, которая вынуждает клиент показать окно ввода пароля
MAI> - если пользователь нажал "отмена" он увидит текст, который идёт за хидером
MAI> с 401-й ошибкой
MAI> - если логин/пароль введён - передаётся в заголовке ответа клиента
MAI> - логин/пароль проверяется по данным СУБД мускл.
Такая схема прекрасно работала в Communiware на протяжении 3-х лет,
пока не пришел в компанию Артем Чуприна и не переделал все нафиг на
куках (стало работать еще прекраснее).
Hо! Тонкость заключается в том, что Communiware это не сgi-скрипт, а
набор mod_perl-овых модулей. Поэтому у меня была возможность залезть
в заголовок Authorization и достать аттуда пароль, дабы проверить его
самому. CGI-скрипту Apache пароля не отдаст.
Поэтому авторизацию надо проводить либо средствами апача, либо
посредством собственного модуля (например перлового, черед mod_perl)
встроенного в апач. Потому что апач и стандартные авторизационные модули
типа mod_auth_mysql, не будут проверять авторизацию для URL, про которую
в конфиге апача не написано AuthType basic.
Есть такой метод обхода этой проблемы (и я им активно пользовался еще
году в 1997):
увидев, что необходима авторизация, скрипт выдает РЕДИРЕКТ на
$cgi->url(-query_string=>0) . "/secure"
В конфиге апача написано
<Location /cgi-bin/myscript/secure>
AuthType basic
и все что касается того по чему проверять пароли
В начале работы скрипт смотрит на path_info, и если там /secure,
то убеждается в наличии $ENV{'REMOTE_USER'}.
После чего уже сам скрипт может решать, заслуживает ли данный юзер
200 или 403.
From: Victor Wagner <[email protected]>
>> Hо! Тонкость заключается в том, что Communiware это не сgi-скрипт, а
>> набор mod_perl-овых модулей. Поэтому у меня была возможность залезть
>> в заголовок Authorization и достать аттуда пароль, дабы проверить его
>> самому. CGI-скрипту Apache пароля не отдаст.
AVG> но если его (apache) прислонить спиной к теплой стенке, то с ним еще можно
AVG> много о чем поговорить (с) А.Райкин
AVG> RewriteEngine on
AVG> RewriteCond %{HTTP:Authorization} ^(.*)
AVG> RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]
Теперь открываем глаза и смотрим, почему разработчики Apache сами этого
не сделали. Ведь им, беднягам, пришлось писать специальный код, который
ВЫКИДЫВАЕТ этот заголовок из числа помещаемых в environment.
Читаем документацию по используемой операционной системе на тему о том,
как узнать Environment неродственного процесса, работающего под тем же
uid, и задумываемся о том, а всем ли авторам CGI-скриптов на данном
сервере, выполняющимся под тем же uid, мы можем доверять.
У меня вопрос не про авторизацию выдачей заголовков из своего скрипта, а вопрос о том, как в браузер своим скриптом отдать поток, начинающийся с HTTP/1.1 мой_код_ответа моё_описание_ответа.
Мой скрипт начинает поток именно такой строкой (HTTP/1.1 ...).
Но в апаче указано "RewriteEngine on" и из-за этого апач вместо потока моего скрипта первую строку потока пытается написать сам, начиная его своим "HTTP/1.1 200 OK", а первую строку потока моего скрипта считает первым http-заголовком и, конечно же, из-за того, что заголовок выглядит не "название: значение", а "HTTP/1.1 код текст" переделывает общий код ответа на HTTP./1.1 500 Internal Server Error с записью в лог ошибки "malformed header from script 'index.cgi': Bad header: HTTP/1.0 404 Not Found".
Мне-то надо скриптом при запросе кем попало левых адресов поток начать строкой "HTTP/1.0 404 Not Found".
А Апач при включенном "RewriteEngine on" строку "HTTP/1.0 404 Not Found" ставит в качестве первого заголовка.
Как мне заставить Апача в таких условиях строку "HTTP/1.0 404 Not Found" делать не http-заголовком, настоящим началом всего потока, чтобы строка "HTTP/1.0 404 Not Found" была реально самой первой строкой ответного потока?