Программа: Контролер злоумышленных пользователей

Скорость работы разделяемой памяти делает ее идеальным выбором для хранения данных, к которым необходим частый доступ со стороны различных процессов веб-сервера, когда файлы или база данных работают слишком медленно. В примере 8.7 показан класс pc_Web_Abuse_

Check, использующий разделяемую память для отслеживания со-единений с веб-сайтом, для того чтобы отсечь пользователей, которые злоупотребляют веб-сайтом, бомбардируя его запросами.

Пример 8.7. pc_Web_Abuse_Check class
class pc_Web_Abuse_Check {
var $sem_key;
var $shm_key;
var $shm_size;
var $recalc_seconds;
var $pageview_threshold;
var $sem;
var $shm;
var $data;
var $exclude;
var $block_message;
function pc_Web_Abuse_Check() {
$this->sem_key = 5000;
$this->shm_key = 5001;
$this->shm_size = 16000;
$this->recalc_seconds = 60;
$this->pageview_threshold = 30;
$this->exclude['/ok-to-bombard.html'] = 1;
$this->block_message =<<
403 Forbidden

Forbidden


You have been blocked from retrieving pages from this site due to
abusive repetitive activity from your account.


If you believe this
is an error, please contact
>webmaster@example.com.


END;
}
function get_lock() {
$this->sem = sem_get($this->sem_key,1,0600);
if (sem_acquire($this->sem)) {
$this->shm = shm_attach($this->shm_key,$this->shm_size,0600);
$this->data = shm_get_var($this->shm,'data');
} else {
error_log("Can't acquire semaphore $this->sem_key");
}
}
function release_lock() {
if (isset($this->data)) {
shm_put_var($this->shm,'data',$this->data);
}
shm_detach($this->shm);
sem_release($this->sem);
}
function check_abuse($user) {
$this->get_lock();
if ($this->data['abusive_users'][$user]) {
// если пользователь находится в списке, освобождаем семафор и память
$this->release_lock();
// выводим страницу"you are blocked"
header('HTTP/1.0 403 Forbidden');
print $this->block_message;
return true;
} else {
// фиксируем пользователя, находящегося на странице в настоящий момент
$now = time();
if (! $this->exclude[$_SERVER['PHP_SELF']]) {
$this->data['user_traffic'][$user]++;
}
// (иногда) идем в начало списка и добавляем плохих людей
if (! $this->data['traffic_start']) {
$this->data['traffic_start'] = $now;
} else {
if (($now - $this->data['traffic_start']) > $this->recalc_seconds) {
while (list($k,$v) = each($this->data['user_traffic'])) {
if ($v > $this->pageview_threshold) {
$this->data['abusive_users'][$k] = $v;
// регистрируем добавление пользователя
// в список злоумышленных пользователей
error_log("Abuse: [$k] (from ".$_SERVER['REMOTE_ADDR'].')');
}
}
$this->data['traffic_start'] = $now;
$this->data['user_traffic'] = array();
}
}
$this->release_lock();
}
return false;
}
}

Для того чтобы с этим классом можно было работать, в начале страницы вызовите метод check_abuse(), передав ему имя зарегистрированного пользователя:

// get_logged_in_user_name() – это функция, которая определяет,
// зарегистрировался ли пользователь
if ($user = get_logged_in_user_name()) {
$abuse = new pc_Web_Abuse_Check();
if ($abuse->check_abuse($user)) {
exit;
}
}

Метод check_abuse() защищает исключительный доступ к сегменту разделяемой памяти, в котором хранится информация о пользователях и трафике, записанная туда с помощью метода get_lock().


Если пользователь уже находится в списке злоумышленных пользователей, то метод освобождает блок в разделяемой памяти, выдает пользовате-
лю страницу ошибки и возвращает значение true. Страница ошибки определяется в конструкторе класса.

Если пользователя нет в списке злоумышленных пользователей и текущая страница (сохраненная в элементе $_SERVER['PHP_SELF']) не входит в список страниц, не подлежащих проверке на злоупотребление, то значение счетчика страниц, просмотренных пользователем, увеличивается. Список страниц, не подлежащих проверке, также определяется в конструкторе. Вызывая метод check_abuse() в начале каждой страницы и помещая страницы, которые не считаются потенциально подверженными злоупотреблению, в массив $exclude, вы тем самым обеспечиваете, что злоумышленный пользователь увидит страницу ошибки даже при просмотре страницы, которая не учитывается при подсчете злоупотреблений. Это делает поведение сайта более уравнове-
шенным.

Следующий раздел функции check_abuse() отвечает за добавление пользователей в список злоумышленных пользователей. Если прошло более $this->recalc_seconds секунд с момента последнего добавления пользователей в список злоумышленных пользователей, то метод
смотрит на счетчики посещения страниц каждого пользователя, и если какой-либо из них превышает значение $this-> pageview_threshold, то они добавляются в список злоумышленных пользователей, а в журнал ошибок помещается сообщение.


Код, который устанавливает $this->data['traffic_start'], если он еще не установлен, выполняется только при самом первом вызове функции check_abuse(). После добавления нового злоумышленного пользователя функция check_abuse() сбрасывает счетчик пользователей и счетчик просмотра страниц и стартует новый интервал до момента следующего обновления списка
злоумышленных пользователей. После освобождения блокировки разделяемой памяти он возвращает false.

Вся информация, необходимая функции check_abuse() для проведения вычислений, т. е. список злоумышленных пользователей, последние значения счетчика просмотренных страниц и время последнего определения злоумышленных пользователей, запоминается в единственном ассоциативном массиве $data. Это делает чтение и запись в разделяемую память более легкой по сравнению с хранением информации в отдельных переменных, поскольку требуется только один вызов функции shm_get_var() и один вызов функции shm_put_var().Класс pc_Web_Abuse_Check блокирует злоумышленных пользователей, но не имеет никакой системы отчетности и не позволяет добавлять в список или удалять из списка отдельных пользователей. Пример 8.8 содержит программу abuse-manage.php, позволяющую манипулировать данными злоумышленных пользователей.

Пример 8.8.


abuse-manage.php
// класс pc_Web_Abuse_Check определен в abuse-check.php
require 'abuse-check.php';
$abuse = new pc_Web_Abuse_Check();
$now = time();
// обрабатываем команды, если они есть
$abuse->get_lock();
switch ($_REQUEST['cmd']) {
case 'clear':
$abuse->data['traffic_start'] = 0;
$abuse->data['abusive_users'] = array();
$abuse->data['user_traffic'] = array();
break;
case 'add':
$abuse->data['abusive_users'][$_REQUEST['user']] =
'web @ '.strftime('%c',$now);
break;
case 'remove':
$abuse->data['abusive_users'][$_REQUEST['user']] = 0;
break;
}
$abuse->release_lock();
// теперь значимая информация находится в $abuse->data
print 'It is now '.strftime('%c',$now).'
';
print 'Current interval started at '.strftime('%c',
$abuse->data['traffic_start']);
print '
('.($now - $abuse->data['traffic_start']).' seconds ago).

';
print 'Traffic in the current interval:
';
if (count($abuse->data['user_traffic'])) {
print '

';
while (list($user,$pages) = each($abuse->data['user_traffic'])) {
print "";
}
print "
UserPages
$user$pages
";
} else {
print "No traffic.";
}
print '

Abusive Users:';
if ($abuse->data['abusive_users']) {
print '

';
while (list($user,$pages) = each($abuse->data['abusive_users'])) {if (0 === $pages) {
$pages = 'Removed';
$remove_command = '';
} else {
$remove_command =
" ">remove";
}
print "";
}
print '
UserPages
$user$pages$remove_command
';
} else {
print "No abusive users.";
}
print<<
Add this user to the abusive users list:





END;

Пример 8.8 выводит информацию о текущем значении счетчика количества посещений страницы пользователем и текущий список злоумышленных пользователей, как показано на рис.


8.1. Он также позволяет добавлять в список или удалять из списка определенных пользователей и очищать весь список.

При удалении пользователей из списка вместо вызова:

unset($abuse->data['abusive_users'][$_REQUEST['user']])
он устанавливает следующую переменную в 0:
$abuse->data['abusive_users'][$_REQUEST['user']]

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

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

Если вы помещаете pc_Web_Abuse_Check и эту страницу поддержки на вашем сервере, то обеспечьте защиту страницы поддержки с помощью пароля или каким-то другим способом от публичного доступа. Очевидно, что от этого кода не очень много толку, если злоумышленные пользователи могут удалить себя из списка злоумышленных пользователей.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела PHP на эту тему:
Аутентификация, основанная на cookies
Буферизация вывода в броузер
Взаимодействие в рамках Apache
Идентификация различных броузеров
Настройка обработки ошибок

Вернуться в раздел: PHP / 8. Основы Web