--------------------------------------------------------------------------------------------------
Организовать простую защиту от
DoS-атак для сайтов, использующих PHP + MySQL, можно следующим образом. В MySQL создается таблица (будем называть ее ip_check) с полями lasttime, count, ip (все поля — типа INTEGER, причем ip — первичный ключ).
Сразу после подключения к базе данных выполняется проверка, есть ли для данного IP-адреса запись в таблице. Если записи нет, она создается, при этом в count пишется 1, в lasttime — текущее время. Если запись уже есть, то проверяется, когда она была сделана (поле lasttime), и если прошло менее определенного количества секунд, то поле count увеличивается на 1, в противном случае приравнивается единице, и производится обновление записи в таблице (записывается новый count и lasttime). Далее происходит проверка величины count, и если она превысила некоторое пороговое значение, выдается статус 500 (или 403 или 503), сообщение об ошибке, и выполнение скрипта завершается.
Таким образом, для выполнения проверки требуется всего одной таблица с предельно простой структурой и 2 запроса SQL. Также можно добавить еще одно поле status, в которое ставить 1, если IP-адрес забанен навсегда по каким-то причинам, и совместить проверку на
DoS-атаки с проверкой на забаненные IP-адреса без добавления дополнительных запросов. При необходимости можно еще одним запросом добавить учет суммарной нагрузки за все время или нагрузки по подсетям.
Кроме того, перед завершением скрипта можно добавить проверку, сколько времени заняло его выполнение, и если оно превышает какую-то пороговую величину (например, 10 секунд), начислять этому IP-адресу “штрафные очки” (т.е. дополнительно увеличивать поле count).
В виде кода это можно представить следующим образом:
Код:
<?php // $link -- соединение с БД, $ip_text -- IP-адрес define('ALERT_TIME',300); define('ALERT_COUNT',150); function check_dos($ip_text, $link) { $ip=ip2long($ip_text); $res=mysql_query($link,'SELECT lasttime, count, status FROM ip_check WHERE ip="'.$ip.'"'); if (mysql_num_rows($res)==0) { // нет такого IP $count=1; mysql_query($link,'INSERT INTO ip_check (lasttime,count,ip,status) VALUES ('.time().',1,"'.$ip.'",0)'); } else { $data=mysql_fetch_row($res); $status=$data[2]; if ($status!=2 && ($data[0]<time()-ALERT_TIME || $data[1]<ALERT_COUNT))) $count=1; elseif ($status!=2) { $count=$data[1]+1; if ($count>=ALERT_COUNT) $status=1; } if ($status!=2) { mysql_query($link,'UPDATE ip_check SET count='.$count,', status='.$status.', lasttime='.time().' WHERE ip="'.$ip.'"'; } if ($status) { header($_SERVER['HTTP_PROTOCOL'].' 503 Temporary Unavailable'); trigger_error('Превышено число допустимых запросов!',E_USER_ERROR); } } ?>
Кроме того, периодически следует очищать базу от старых записей (например, по cron). Кроме того, если ожидается большая интенсивность атак, возможно, имеет смысл заменить UPDATE-запрос на последовательность DELETE/INSERT, но в этом случае необходимо будет регулярно проводить оптимизацию таблицы.
--------------------------------------------------------------------------------------------------
[FAQ] AntiDDOS/DOS
Онтеддос скрипт:
Код:
#!/bin/bash
/sbin/modprobe ip_tables
/sbin/modprobe ip_conntrack
/sbin/modprobe ip_conntrack_ftp
rm /root/.dyn*
echo "Setting kernel tcp parameters to reduct DoS effects"
#Reduce DoS'ing ability by reducing timeouts
echo 15 > /proc/sys/net/ipv4/tcp_fin_timeout
echo 18 > /proc/sys/net/ipv4/tcp_keepalive_time
echo 1 > /proc/sys/net/ipv4/tcp_window_scaling
echo 0 > /proc/sys/net/ipv4/tcp_sack
echo 128 > /proc/sys/net/ipv4/tcp_max_syn_backlog
#ANTISPOOFING
for a in /proc/sys/net/ipv4/conf/*/rp_filter;
do
echo 1 > $a
done
##
#NO SOURCE ROUTE
for z in /proc/sys/net/ipv4/conf/*/accept_source_route;
do
echo 0 > $z
done
#SYN COOKIES
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
#echo $ICMP_ECHOREPLY_RATE > /proc/sys/net/ipv4/icmp_echoreply_rate
echo "1" > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
echo "1" > /proc/sys/net/ipv4/conf/all/accept_redirects
echo "1" > /proc/sys/net/ipv4/conf/all/log_martians
# NUMBER OF CONNECTIONS TO TRACK
echo "65535" > /proc/sys/net/ipv4/ip_conntrack_max
# Set default policies
/sbin/iptables -P INPUT ACCEPT
/sbin/iptables -P OUTPUT ACCEPT
/sbin/iptables -P FORWARD DROP
/sbin/iptables -F
/sbin/iptables -F INPUT
/sbin/iptables -F OUTPUT
/sbin/iptables -F FORWARD
/sbin/iptables -F -t mangle
/sbin/iptables -X
/sbin/iptables -A INPUT -i lo -j ACCEPT
/sbin/iptables -A INPUT -d 127.0.0.0/8 -j REJECT
/sbin/iptables -A INPUT -i eth0 -j ACCEPT
/sbin/iptables -A INPUT -m state --state INVALID -j DROP
### chains to DROP too many SYN-s ######
/sbin/iptables -N syn-flood
/sbin/iptables -A syn-flood -m limit --limit 1/second --limit-burst 2 -j RETURN
/sbin/iptables -A syn-flood -j LOG --log-prefix "SYN flood: "
/sbin/iptables -A syn-flood -j DROP
--------------------------------------------------------------------------------------------------
Простенький скрипт от nod'a, для защиты от ddos'a (только http flood)
Код:
$ip = $REMOTE_ADDR;
if (!in_array($ip,array('66.249.64.', '81.19.64.', '89.208.19.', '213.180.206.', '72.30.0.', '87.250.230.', '213.180.216.', '222.0.0.'))) //Ипы поисковиков, которым открыт доступ к сайту.
{
if (!isset($_COOKIE["Тут пиши имя кукиса"]) || ($_COOKIE["Тут пиши имя кукиса"]!="А тут содержание кукиса"))
{
$location="http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$e = substr($location,0,strlen($r)-1);
if ($location==$e."s")
{
die("Сработала анти ДДос защита. Обратитесь к администрации. ICQ 570855 (core32.org[/url])");
}
setcookie("Тут пиши имя кукиса","А тут содержание кукиса", time()+8640000);
Header("Location: ".$location."?ddos=ddos");
exit;
}
}
--------------------------------------------------------------------------------------------------
Код:
<?php
/*
////////////////////////////////////////////////////////////
Antiddos php module \=
РАБОТАЕТ НА IPTABLES!!!
Вставлять в начало скрипта через include
Для работы антиддоса надо иметь право доступа к исполенению функции system и команды iptables(в данном случае) если же такого нет то, то *** знает чето сделал йа.
лучше ставить антиддос на впс или выделенный сервер.
От средненькогл ддосика поможет.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
*/
$debug = false; // дебуг мод, отключает бан, показывает просто сообщения, забанило али нет.
if($debug){
error_reporting(E_ALL);
}else{
error_reporting(0);
}
/* Возможные значения $ddos 1-5:
| 1. Простая проверка по кукам, по умолчанию(рекомендую)
| 2. Двойная проверка через гет antiddos и meta refresh
| 3. Запрос на авторизацию WWW-Authenticate
| 4. полное отключение сайта, боты не блокируются!!!
| 5. выключать сайт если нагрузка слишком большая на сервере, боты не блокируются!!!
*/
$ddos = 1;
$log = false;
$dir = dirname(__FILE__).'/cyki_bots/'; //дира с логами ддоса, создавать и ставить права 777
$ddos_redirect_host = 'http://google.com/'; // хост на который редиректить ддос.
$icq = '123456'; //ася админа
$off_message = 'Извините, тех неполадки.'; //сообщение при выключенном сайте
$anticyka = md5(sha1('botik' . strrev(getenv('HTTP_USER_AGENT'))));
$ban_message = 'Вы были заблокированы. Если это ошибка обратитесь к администратору, icq of admin:'.$icq.'<hr>(c)****** antiddos module';// сообщение при бане
$exec_ban = "iptables -A INPUT -s ".$_SERVER["REMOTE_ADDR"]." -j DROP"; // для iptables, если у вас другая система измените, не лезьте если не знаете, лучше проконсультироваться!!!
$load = sys_getloadavg(); // функция для получения лоад авереге \=\
$ddosuser = 'lol_ddos';
$ddospass = substr(ip2long($_SERVER['REMOTE_ADDR']), 0, 4);
//не тестил //смотрит не поисковики ли это:
$google = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "googlebot.com") !== false;
$yandex = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "yandex.ru") !== false;
$rambler = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "ramtel.ru") !== false;
$rambler2 = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "rambler.ru") !== false;
$aport = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "aport.ru") !== false;
$sape = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "sape.ru") !== false;
$msn = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "msn.com") !== false;
$yahoo = strpos(gethostbyaddr($_SERVER['REMOTE_ADDR']), "yahoo.net") !== false;
//
$f1 = fopen($dir . 'banned_ips', "a");
fclose($f1);
if(strstr (file_get_contents($dir . 'banned_ips'), $_SERVER['REMOTE_ADDR'])){
die($ban_message);
}
if(!$google || !$yandex || !$rambler || !$rambler2 || !$aport || !$sape || !$msn || !$yahoo)
{
$f = fopen($dir . $_SERVER["REMOTE_ADDR"], "a");
fwrite($f, "zapros cyka\n");
fclose($f);
function ban()
{
if(!system($exec_ban)){
$f = fopen($dir . 'banned_ips', "a");
fwrite($f, $_SERVER['REMOTE_ADDR'].'|');
fclose($f);
}
echo $ban_message;
header('Location: '.$ddos_redirect_host.'');
die();
}
switch($ddos){
///////////////////////////
case 1:
if (empty($_COOKIE['ddos']) or !$_COOKIE['ddos'])
{
$counter = @file($dir . $_SERVER["REMOTE_ADDR"]);
setcookie('ddos', $anticyka, time() + 3600*24*7*356); // на год нах.
if(count($counter) > 10)
{
if(!$debug){
ban();
}else{
die ("Блокированы.");
}
}
if(!$_COOKIE['ddos_log']=='bil'){
if(!$_GET['antiddos'] == 1){
setcookie('ddos_log', 'bil', time()+3600*24*7*356); //типо запрос уже был чтоб не перекидывало постоянно рефрешем.
header("Location: ./?antiddos=1");
}
}
}elseif ($_COOKIE['ddos'] !== $anticyka)
{
if(!$debug){
ban();
}else{
die ("Блокированы.");
}
}
break;
/////////////////////////
case 2:
if(empty($_COOKIE['ddos'])){
if(empty($_GET['antiddos'])){
if(!$_COOKIE['ddos_log']=='bil'){ //проверям есть ли запись в куках что был запрос
die('<meta http-equiv="refresh" content="0;URL=?antiddos='.$anticyka.'" />');
}
}elseif($_GET['antiddos'] == $anticyka){
setcookie('ddos', $anticyka, time()+3600*24*7*356);
setcookie('ddos_log', 'bil', time()+3600*24*7*356); //типо запрос уже был чтоб не перекидывало постоянно рефрешем.
}else{
if(!$debug){
ban();
die("May be shall not transform address line?");
}else{
echo "May be shall not transform address line?";
die ("Блокированы.");
}
}
}
break;
case 3:
if(!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER']!== $ddosuser || $_SERVER['PHP_AUTH_PW'] !== $ddospass){
header('WWW-Authenticate: Basic realm="Vvedite parol\': '.$ddospass.' | Login: '.$ddosuser.'"');
header('HTTP/1.0 401 Unauthorized');
if(!$debug){
ban();
}else{
die ("Блокированы.");
}
die("<h1>401 Unauthorized</h1>");
}
break;
case 4:
die($off_message); //сайт отключен
break;
case 5:
if ($load[0] > 80) {
header('HTTP/1.1 503 Too busy, try again later');
die('<center><h1>503 Server too busy.</h1></center><hr><small><i>Server too busy. Please try again later. Apache server on '.$_SERVER['HTTP_HOST'].' at port 80 with <a href="http://forum.**********/">ddos protect</a></i></small>');
}
break;
default:
break;
//////////////////////////
}
if ($_COOKIE['ddos'] == $anticyka)
{
@unlink($dir . $_SERVER["REMOTE_ADDR"]);
}
}
//////////////////////////////
//превед всем кто меня знает о_0
?>
Добавлено: теперь поисковые боты определяются другим алгоритмом.
Текущая версия: 1.1