Сделай Сам Свою Работу на 5

Использование настроек Apache





 

Часто возникает вопрос – как закрыть директорию со скрпитами администрирования паролем. При этом не нужно никаких изысков – один или несколько администраторов имеют одни и те же права, а персоналии меняются редко. Проще всего в данной ситуации использовать стандартную серверную авторизацию — положить файлы .htaccess и .htpasswd и прописать в них нужные параметры.

Итак, вы создаете в каталоге, доступ к которому планируется ограничить, файл .htaccess, после чего вставляете в него следующие директивы:

· AuthType тип контроля – обычно используется Basic.

· AuthName имя – задаёт имя области, в которой действительны имена и пароли пользователей. Это то самое имя, которое браузер показывает в диалоге ввода пароля. Задав одно такое имя для разных каталогов, можете сэкономить пользователям время по вводу лишнего пароля.

· AuthGroupFile имя – задаёт имя файла, в котором хранятся имена групп и их членов. Его формат:

 

group1: member1 member2 ...

group2: member3 member4 ...

 

· AuthUserFile имя – задаёт имя файла с паролями. По большому счёту для его формирования надо воспользоваться утилитой htpasswd из поставки Apache. Но по крайней мере для некоторых версий сервера этот формат такой:



 

user1:passwordhash1

user2:passwordhash2

 

Passwordhash вполне можно получить стандартной функцией Perl'а:

 

$hash=crypt($pass,$salt);

 

где $pass - пароль, $salt - строка из двух символов, участвующая в формировании хэша.

Так что вполне можно автоматизировать процесс добавления новых пользователей, смену паролей через html-формы и т.д.

 

· require user user1 user2 и require group user1 user2 позволяют указать, какие пользователи и группы получат доступ к данному каталогу.

· require valid-user разрешает доступ всем пользователям, указанным в файле паролей системы.

· <Limit method1 method2 ...> ... </Limit> , где methodi определяет HTTP-метод. Например, <Limit GET POST> ограничивает применение вложенных в нее директив случаями использования методов GET и POST (обычно этого более чем достаточно). Вложенными могут быть директивы require, order, allow и deny.

· Ещё пара полезных директив – deny и allow – соответственно запрещения и разрешения доступа. Применяются примерно так:

 

deny from all

allow from 192.168

 

По умолчанию сначала выполняются все deny, потом все allow, так что allow from all разрешит доступ всем пользователям, невзирая ни на какие deny. Порядок можно изменить директивой order: order allow, deny.



· deny from all отлично сочетается со вторым способом защиты страниц через CGI, именно этой директивой лучше всего прикрывать всякие пароли к гостевым книгам и т.д. При попытке обращения к страницам из этого каталога пользователь получит сообщение об отсутствии прав доступа.

 

Кстати, тут между делом демонстрируется самостоятельная обработка ошибок: в данном случае - код 403, Forbidden. Аналогично обрабатывается и всеми любимая 404, Not Found, и 401, Unauthorized. Для этого достаточно добавить в .htaccess директиву ErrorDocument код url:

 

ErrorDocument 404 /cgi-bin/bad.pl

ErrorDocument 403 /cgi-bin/badaccess.pl

ErrorDocument 401 /cgi-bin/badaccess.pl

 

Всё, что делает скрипт – формирует сообщение об ошибке, используя переменную окружения REQUEST_URI, так что вместо него вполне можно просто указать какую-нибудь подходящую страницу.

Для заключительного примера используем файл .htaccess со следующим содержимым:

 

AuthType Basic

AuthName Test

AuthGroupFile /.../pagepsw/deny/tgroup

AuthUserFile /.../pagepsw/deny/tuser

<Limit GET POST>

require group test

</Limit>

 

В файле tgroup всего одна строчка - test: login test, в файле tuser - зашифрованные пароли для login (password) и test (test). Обратите внимание, при повторном обращении к этой странице браузер понимает, что только что обращался к этой области, и не утруждает пользователя лишним запросом пароля.

Добавим ещё два момента. Первое – это куда класть файл .htpasswd. Экспериментальным путем было выяснено, что если, например, путь к документу с сообщением об ошибке (ErrorDocument) пишется относительно системной переменной DocumentRoot, то путь к файлу с паролями (UserFile) пишется относительно ServerRoot. Выше ServerRoot положить .htpasswd нельзя – "../" не воспринимается. Всё это сделано для того, чтобы можно было поместить файл с паролями, например, одним уровнем выше корневой директории сайта, чтобы из сети доступа к файлу не было вообще.



Второе – это то, что скрипт может узнать, кто его открывает и пароль: переменные $PHP_AUTH_USER и $PHP_AUTH_PW.

Главный недостаток этого способа – сервер не может блокировать подбор пароля. Ещё один недостаток – необходимость переписывать файлы с паролями при удалении пользователя или введении нового. Но если это происходит нечасто, этого способа вполне достаточно, к тому же не придётся забивать голову написанием механизма авторизации.

 

Автоматизация авторизации

 

Это нужно не только для упрощения работы с большим количеством пользователей и их большой "текучкой". Если нужно держать дополнительную информацию о пользователях, либо необходимо гибкое разграничение прав, лучше перенести авторизацию на БД.

Каждая страница закрытой территории подключает файл с вот таким кодом (пример 8.3.2.1):

 

$result = mysql_query("

SELECT * FROM person WHERE

login='". preg_replace("/[^\\w_-]/","",$PHP_AUTH_USER). "'

AND pass='". md5($PHP_AUTH_PW). "'");

if (@mysql_num_rows($result)!=1) {

header("WWW-Authenticate: Basic realm=\"User area\"");

header("HTTP/1.0 401 Unauthorized");

print("Чтобы войти в пользовательскую часть сайта, надо ввести имя и пароль.");

exit();

};

 

$user_row = mysql_fetch_array($result);

 

В первой строке из логина удаляются все символы кроме букв, цифр, тире и символа подчеркивания. Затем проверяется количество полученных строк, и только если это одна строка, дается доступ. В остальных случаях пользователь увидит в браузере окно, предлагающее ввести логин и пароль. Если же пользователь вошёл успешно, в массиве $user_row мы имеем всю информацию о нем.

Конечно же, этот пример имеет ряд существенных недостатков, т.к.:

· защиты от подбора здесь нет;

· если таблица пользователей большая, при подборе пароля злоумышленник, скорее всего, "завалит" базу.

 

Хранение зашифрованных данных в cookies

 

Есть скрипт для входа, остальные подключают код, позволяющий только продолжить действия в закрытой области – если cookies истекут, или он выйдет оттуда, придётся возвращаться на страницу для входа.

Входной скрипт проверяет логин и пароль и выдает две cookies. В первой – логин, чтобы сразу опознать пользователя (в базе поле логина, естественно, уникальное или даже ключевое). Во второй cookie — хэш от времени входа и пароля.

Все остальные программы подключают код, который делает запрос в базу – выбирает строку с полученным логином. Из этой строки берет поле "log_time" и пароль и делает из них хэш. Сравнивает его с тем, что получил, и если они совпадают, выдает новую cookie хэша, опять же, от пароля и времени и делает запрос в базу данных (пример 8.3.3.1):

 

"UPDATE user SET log_time='...' WHERE login='$cookie_login'".

if (isset($HTTP_COOKIE_VARS[$cookie_login]) && isset($HTTP_COOKIE_VARS[$cookie_code])) {

 

$login = $HTTP_COOKIE_VARS[$cookie_login];

$code = $HTTP_COOKIE_VARS[$cookie_code];

 

$result = mysql_query("SELECT date_format(log_date,'%Y%m%d%H%i%s') as log_date1,pass,uid

FROM user WHERE email='$login' AND log_date>'DATE_SUB(NOW(),INTERVAL 15 MINUTE)'");

if (!mysql_error() && @mysql_num_rows($result)==1) {

$log_time0 = time();

$log_time1 = date("YmdHis", $log_time0);

$log_time2 = date("Y-m-d H:i:s", $log_time0);

$current_user = mysql_fetch_array($result);

if (md5($current_user["pass"].$current_user["log_date1"].$md5letter) == $code) {

mysql_query("UPDATE user SET log_date='$log_time2' WHERE uid=".$current_user["uid"]);

setcookie($cookie_code, md5($current_user["pass"].$log_time1.$md5letter),

time()+900, $site_path);

$auth = true;

}

else

unset($current_user);

};

};

 

 

Блокировка подбора

 

Пароль длиной десять символов из букв латиницы и цифр – это очень много вариантов. Но поскольку такую абракадабру запомнить сложно, мы чаще делаем пароль из осмысленных слов. Несколько лет назад оказалось, что большинство паролей можно подобрать при помощи словаря из 10 000 слов. В своё время в сети появился червь, который лазил по юниксовым серверам, используя их дырки в защите, и подбирал пароли привилегированных пользователей при помощи системного орфографического словаря юникса.

Каждый пользователь, пока он не ввёл правильный логин и пароль, считается злобным хакером. С чем же мы имеем дело, когда пользователь вводит что-либо неправильно? Забывчивость (на это на приличных сайтах есть формочка "забыл пароль", чтобы отправить на введёный в системных настройках email этот самый пароль), баловство, подбор пароля по словарю (вероятность удачного подбора велика, поэтому закрывать надо, тем более, если сайт коммерческого характера) или DoS-атака (чтобы не перегрузить сервер, надо минимизировать действия, которые будет выполнять скрипт в таком случае)

Как можно вызвать перегрузку на сервере, если механизм защиты стоит на файлах? Оказалось, несложно (сколько это будет стоить – другой вопрос). Итак, допустим, сервер не выдержит, если скрипт будет пытаться 1000 раз в секунду открывать файлы на запись и писать в них данные. Поскольку после 5 неудачных попыток войти в систему пользователь будет сразу получать отказ в доступе (без какой-либо записи данных в файл), надо найти 200 уникальных IP, с которых по пять раз и обратиться. Это возможно. Вешаем в баннерокрутилке html-баннер с пятью тегами:

 

<img src="http://user:password@www.host.ru/secret/absent.gif" width=1 height=1>

 

Пользователь моментально делает пять обращений, сервер пять раз пишет в файл (кстати, в некоторых браузерах, возможно, выскочит окно для ввода логина и пароля). Можно сделать html-страницу с пятью такими картинками, а саму страницу вставить через iframe на посещаемый сайт (через iframe – чтобы по полю referer не нашли). Эти примеры, разумеется, натянуты, но сам факт того, что можно воспользоваться таким недостатком системы, доказан.

Рассмотрим способ борьбы с этим явлением (пример 8.3.4.1):

 

$errors = 0;

$fn = "ignore/". preg_replace("[^\d\.]", "", $REMOTE_ADDR. ".". $HTTP_FORWARDED_FOR);

 

if (is_file($fn)) {

if (filectime($fn) < time()-3600)

unlink($fn);

else

$errors = fread(fopen($fn, "r"), 2);

};

 

if ($errors>5) {

print ("Доступ закрыт. Зайдите через час.");

exit();

};

// здесь происходит установка связи с сервером БД. чтобы не трогать зря, если пользователя сразу же "отлупили".

$result = mysql_query("SELECT * FROM user WHERE

login='". preg_replace("/[^\w_\-]/", "", $PHP_AUTH_USER). "' AND

pass='". md5($PHP_AUTH_PW). "'");

if (@mysql_num_rows($result)!=1) {

header("WWW-Authenticate: Basic realm=\"secret area\"");

header("HTTP/1.0 401 Unauthorized");

print ("Authorization required");

fwrite(fopen($fn, "w"), ++$errors);

exit();

};

 

$current_user = mysql_fetch_array($result);

mysql_free_result($result);

 

Для непрошедших авторизаций создаём таблицу:

 

CREATE TABLE unauth (username VARCHAR(64) NOT NULL, pass VARCHAR(64) NOT NULL, ip VARCHAR(255), logintime TIMESTAMP)

И вместо обращения к файлам работаем с базой.

$errors = @mysql_result(mysql_query("SELECT count(username) as falses FROM unauth WHERE

logintime>DATE_SUB(NOW(),INTERVAL 1 HOUR) AND ip='$REMOTE_ADDR'"),0);

if (mysql_error())

die(mysql_error());

 

if ($errors>5) {

print ("Доступ закрыт. Зайдите через час.");

exit();

};

$result = mysql_query("SELECT * FROM user WHERE

login='". preg_replace("/[^\w_\-]/", "", $PHP_AUTH_USER). "' AND

pass='". md5($PHP_AUTH_PW). "'");

if (@mysql_num_rows($result)!=1) {

header("WWW-Authenticate: Basic realm=\"secret area\"");

header("HTTP/1.0 401 Unauthorized");

print ("Authorization required");

mysql_query("INSERT INTO unauth (username, pass, ip) VALUES

('$PHP_AUTH_USER', '$PHP_AUTH_PW', '$REMOTE_ADDR $HTTP_X_FORWARDED_FOR')");

exit();

};

 

$current_user = mysql_fetch_array($result);

mysql_free_result($result);

 

Хранить ли старые записи для статистики или нет – дело хозяйское. Если что, их можно удалять, выполняя перед авторизацией запрос:

 

DELETE FROM unauth WHERE logintime<DATE_SUB(NOW(),INTERVAL 1 HOUR)

 

Такой механизм при больших нагрузках будет работать быстрее и надёжнее, чем файлы – в базе часто используемые данные буферизуются и обрабатываются непосредственно в оперативной памяти.

 

 








Не нашли, что искали? Воспользуйтесь поиском по сайту:



©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.