![]() |
![]() |
| http://kurepin.ru/php/zametki/flood/ |
|
Некоторое время назад передо мной встала проблема безопасности несколько острее, чем стояла до сих пор. Дело в том, что мне пришлось программировать проект, который уже много лет было модно взламывать. "Ко01}{ацкеров" развелось достаточно много и они беспрестанно думают, что бы такого сделать плохого. Путей насолить обычно бывает огромное множество, программисту практически не реально поставить стопроцентно надежный заслон на каждом из них. Но к этому нужно стремиться. Я хочу обсудить пока один из аспектов защиты. Речь пойдет о защите от flood-атак, от одной из их разновидностей. Flood - это атака, при которой атакующий заваливает сервер частыми запросами. Разновидностей таких атак существует огромное множество, бывают атаки на уровне TCP/IP, бывают на уровне прикладных протоколов типа HTTP, FTP и т.п. Бывают атаки распределенные, когда атака производится с множества адресов, с такими атаками очень сложно бороться; бывали случаи, когда такими атаками доводили до банкротства вполне серьезных и крупных провайдеров. В данном конкретном случае, я выступаю как простой веб-программист, посему борьба с такими тяжелыми случаями ложится не на мои плечи - это забота сисадминов сервера (может по этому они такие злые?) В свою очередь, я как программист, могу защититься от атак попроще, до которых сисадминам обычно нет дела. (нет дела до тех пор, пока сервер не ляжет). Поясню на примере гостевой книги. В каждой гостевой книге есть форма для постинга сообщений. Обычно безопасности гостевых книг уделяется не слишком много внимания. Между тем зловредный хацкер может навредить и здесь. Например, можно за несколько секунд организовать автоматический поток бессмысленных сообщений в гостевую книгу. Чем это грозит? Например, может кончиться выделенное пространство на диске, вебсервер может начать использовать слишком много процессорных ресурсов, за что провайдер может отключить хостинг, ну и на конец, после этого гостевая будет не слишком красиво выглядеть. Вообще, гостевую я привел как простейший пример. На самом деле атаковать таким образом можно практически любой скрипт. Соответственно, и последствия атаки могут быть самыми разнообразными. Вот передо мной и встала проблема защититься от такой ситуации. Ниже приведенный текст php-скрипта содержит функцию, которая призвана помочь побороть данную проблему. Технология простая - функция считает кол-во обращений с одного IP-адреса в интервал времени, и при превышении лимита возвращает ненулевое значение. Функцию можно использовать для контроля в нескольких местах с разными именами файлов, разными интервалами и лимитами.
// проверка на превышение частоты обращения к скрипту с одного IP-адреса
// (c) Oleg V. Konstantinov 2003.
// syntax: $res = freq_limit($filename,$ip,$interval,$limit);
// $filename имя файла для хранения
// $ip адрес текущего запроса
// $interval интервал ограничения
// $limit максимальное кол-во обращений за интервал
// $res кол-во запросов, если есть превышение частоты, иначе 0;
function freq_limit($filename,$ip,$interval,$limit){
$lip = ip2long($ip);
$tmp = array(); // массив для временного хранения
$requests = 1; // счетчик запросов с данного IP в течении интервала,
// 1 - учитываем текущий запрос
//читаем файл, открывая его для последующей записи
$in = fopen($filename,"r+");
if($in){
flock($in,LOCK_EX ) or die("Cannot flock file.");
$now = time();
while($block = fread($in,8)){
//каждая запись - ip,time в двоичном формате
$arr = unpack("Lip/Ltime",$block);
//если обращение прокисло(прошло время больше интервала) игнорируем
if( ($now - $arr['time']) > $interval ){
continue;
}
// если в течении интервала был запрос с текущего IP, увеличиваем счетчик
if($arr['ip'] == $lip){
$requests++;
}
$tmp[] = $arr;
}
//обнуляем файл, для последующей записи
fseek($in,0);
ftruncate($in,0);
}else{ //если нет файла, создаем его
$in = fopen($filename,"w");
flock($in,LOCK_EX ) or die("Cannot flock file.");
}
//заливаем массив
for($i=0;$i < count($tmp);$i++){
fwrite($in,pack('LL',$tmp[$i]['ip'],$tmp[$i]['time']));
}
//записываем текущий запрос
fwrite($in,pack('LL',$lip,$now));
// закрываем файл, блокировка снимается автоматически
fclose($in);
// если лимит превышен возвращаем кол-во обращений, иначе 0
return ($reqs > $limit)?$reqs:0;
}
// Собственно скрипт. Разрешает выполнять с одного IP-адреса до 5 запросов
// в минуту.
if(freq_limit("freq-req.bin", getenv("REMOTE_ADDR"),60,5)){
echo "Абламайтес!";
exit;
}
echo "Усе пучком!";
Материал подготовил 16.02.03 |