Ключевые моменты:
* Реализация скрипта для проверки PTR посетителей;
* Конфигурирование nginx в IfIsEvil-style с ветвлениями map;
* Имена location в переменных map;
* Управление ветвлением через try_files /nonexist $map_var.
Многие высоконагруженные и популярные сайты страдают от того, что кроме живых посетителей их посещают разнообразные парсеры, боты и прочие автоматические сканеры, которые не несут никакого полезного эффекта, а только создают паразитный трафик и нагрузку на, и без того, нагруженную систему. В данном случае я не имею виду поисковых ботов, которые хоть и зачастую нагружают проект не нормировано, но просто необходимы любому проекту.
Один из наших клиентов регулярно испытывал проблему лавинообразного роста нагрузки в определенное время суток. Периодически, раз в сутки и чаще происходили наплывы посещений со значительным ростом LA на серверах. Было принято решение построить защиту от паразитного трафика.
Мы обнаружили, что у паразитного трафика имеются определенные паттерны поведения и свойства, а именно:
* По источнику запросов – подсети Amazon, Tor;
* По точкам входа – большинство запросов к разделу товаров;
* По UserAgent – основная часть ботов отправляли UA поисковиков Google, Yandex, Bing, но объективно не являлись ими;
* По рефереру – в основном реферер был пустой.
Реализованные проверки, ограничения и блокировки:
* Вручную добавили заранее известные IP в белый список
* И ранее скомпрометированные IP – в черный список
* Ограничение limit_req_zone для всех, кроме белых списков
* Проверка посетителей с UA поисковиков на соответствие PTR-записи настоящим PTR-записям поисковиков и помещаем в белый список прошедших проверку и всегда разрешаем их.
* При повышении LA порога «Атака»:
** Не прошедших проверку UA по PTR мы заносим в черный список и блокируем.
** Проверяем повторяемость рефереров в access-логе и блокируем посетителей с реферером, превысившим заданное количество вхождений
** Остальных проверяем капчей и разрешаем в случае успеха.
Первые три пункта это распространенные решения, поэтому подробнее остановлюсь на на проверке посетителей по PTR, конфигурировании nginx c использованием конструкции try_files /nonexist $map_var и сложных map.
Мы реализовали скрипт для асинхронной проверки посетителей с UA поисковиков на соответствие PTR-записи настоящим PTR-записям поисковиков.
Он запускается по cron раз в минуту. По уникализированному списку IP посетителей с UA поисковика производит проверку PTR и сверяет доменное имя второго уровня. Если домен совпадает, то добавляет IP в белый список, иначе в черный список. При проверке списка, скрипт не проверяет PTR уже проверенных ранее IP для ускорения процесса проверки. Это позволяет проходить по списку IP из access-лога ежеминутно даже при высокой скорости наполнения access-лога. Записи в черный список заносятся с указанием фиксированного времени удаления из списка блокировки для исключения постоянной блокировки общих IP в больших NAT сетях.
Таким образом мы формируем и поддерживаем файлы ptr_blacklist.map и ptr_whitelist.map которые инклудятся в конфиг nginx.
Запускается ежеминутно.
#!/bin/bash
# Базовые настройки скрипта, обычно выносятся в отдельный файл,
# чтобы их можно было подстраивать под конкретный проект, не меняя основной скрипт.
# Export inc file for nginx
EXPORT_MAP=true
# Domain list
DOMAIN_LIST="domain"
# Block time (in minutes)
BLOCK_TIME=1440
# White list IP
IP_WHITELIST=""
# White list PTR
BOTS="google|yandex|bing|Bing|msn"
# false - not block IP if there is a PTR record
BLOCK_WITH_PTR=true
UNBLOCK_ENABLE=true
LOGFILE=/var/log/ua-table.log
LOGFILE2=/var/log/ua-table-history.log
LOCK=/tmp/ua_check.lock
D=$DOMAIN_LIST
# Скрипт формирует ptr_blacklist и ptr_whitelist и только потом копирует их в map-файлы
# для минимизации блокировки рабочих файлов
BL_FILE=/etc/nginx/vhosts.d/ptr_blacklist
WL_FILE=/etc/nginx/vhosts.d/ptr_whitelist
BL_FILE_MAP=$BL_FILE.map
WL_FILE_MAP=$WL_FILE.map
TMP_LOG=/tmp/$D-acc-temp.log
TMP_LOG1=/tmp/$D-acc-temp1.log
NGINX_LOG=/srv/www/$D/shared/log/$D-acc.log
[ ! -f /usr/bin/host ] && echo "/usr/bin/host not found. Please yum install bind-utils" && exit
[ -z "$DOMAIN_LIST" ] && echo "DOMAIN_LIST is empty"
[ ! -f $LOGFILE ] && touch $LOGFILE
[ ! -f $LOGFILE2 ] && touch $LOGFILE2
debug="0"
function e {
echo -e $(/bin/date "+%F %T") $1
}
# Проверяем не запущен ли скрипт, это позволяет не дублировать затянувшиеся проверки
[ -f $LOCK ] && e "Script $0 is already runing" && exit
/bin/touch $LOCK
DT=`/bin/date "+%F %T"`
if [ ! -f $NGINX_LOG ];then
echo "Log ($NGINX_LOG) not found."
/bin/rm -rf $LOCK
exit
fi
# Основная часть скрипта
# Делаем выборку из acc-лога, регистр важен, так мы не цепляем записи с вхождением в referer
/bin/egrep "Yandex|Google|bingbot|Bing" $NGINX_LOG | /usr/bin/awk '{print $1}' | /bin/sort -n | /usr/bin/uniq > $TMP_LOG
if [ "$EXPORT_MAP" == "true" ]; then
[ ! -f $BL_FILE_MAP ] && /bin/touch $BL_FILE_MAP
[ ! -f $WL_FILE_MAP ] && /bin/touch $WL_FILE_MAP
[ ! -f $BL_FILE ] && /bin/touch $BL_FILE || /bin/cp -f $BL_FILE $BL_FILE.bak
[ ! -f $WL_FILE ] && /bin/touch $WL_FILE || /bin/cp -f $WL_FILE $WL_FILE.bak
fi
# Разблокируем адреса
UNBLOCK=0
while read line
do
if [[ "$line" == *=* ]]; then
GET_TIME=`echo $line | /usr/bin/awk -F"=" '{print $2}'`
NOW=`/bin/date '+%s'`
#echo $NOW
#echo $GET_TIME
if [ "$NOW" -gt "$GET_TIME" ]; then
IP=`echo $line | awk '{print $3}'`
e "$IP unblocked." >> $LOGFILE2
/bin/sed -i '/'$IP'/d' $BL_FILE
/bin/sed -i '/'$IP'/d' $LOGFILE
UNBLOCK=1
#else
#e "Nothing to unblock" >> $LOGFILE2
fi
fi
done < $LOGFILE
# Блокируем адреса
while read line
do
IP=$line
wl=0
bl=0
# входит в ручной WL
for I in $IP_WHITELIST
do
if [ "$I" = "$IP" ];then
wl=1
fi
done
# ранее уже проверен и внесен в WL
for I in $(/usr/bin/awk '{print $1}' < "$WL_FILE" )
do
if [ "$I" = "$IP" ];then
wl=1
fi
done
# ранее уже проверен и внесен в BL
for I in $(/usr/bin/awk '{print $1}' < "$BL_FILE" )
do
if [ "$I" = "$IP" ];then
bl=1
fi
done
# Если IP есть в списках, значит ранее уже проверен, не проверяем его PTR
if [ "$wl" = "1" -o "$bl" = "1" ]; then
[ "$debug" -gt "1" ] && e "$IP in white or black list" >> $LOGFILE2
else
PTR=""
SRCHBOT=""
FINDPTR="`/usr/bin/host $IP | /bin/grep -v 'not found' | /bin/grep -v 'no PTR record' | /usr/bin/head -1 | /usr/bin/awk '{ print $5 }' | /bin/sed 's/\.$//'`"
if [ -z "$FINDPTR" ];then
PTR=" (PTR record not found)"
else
PTR=" ($FINDPTR)"
fi
SRCHBOT=`/usr/bin/host $IP | /usr/bin/awk '{ print $5 }' | /usr/bin/rev | /usr/bin/cut -d . -f 2-3 | /usr/bin/rev | /bin/egrep "$BOTS"`
[ -n "$SRCHBOT" ] && BOT="YES" || BOT="NO"
[ -z "$BLOCK_WITH_PTR" ] && BLOCK_WITH_PTR=true
if [ "$EXPORT_MAP" == "true" ]; then
if [ "$BOT" == "NO" ]; then
e "$IP blocked $BLOCK_TIME minutes. ($D) Unblock = `/bin/date --date="$BLOCK_TIME minute" +%s`" >> $LOGFILE
e "$IP$PTR blocked $BLOCK_TIME minutes. ($D)" >> $LOGFILE2
echo "$IP 0;" >> $BL_FILE
else
echo "$IP 1;" >> $WL_FILE
fi
fi
fi
done < $TMP_LOG
# часть проверки и подмены map-файлов
if [ "$EXPORT_MAP" == "true" ]; then
/bin/sort -u -o $BL_FILE $BL_FILE > /dev/null 2>&1
/bin/sort -u -o $WL_FILE $WL_FILE > /dev/null 2>&1
MAP_CHANGED=0
if ! diff $BL_FILE $BL_FILE.bak > /dev/null 2>&1; then
/bin/cp -f $BL_FILE_MAP $BL_FILE_MAP.bak > /dev/null 2>&1
/bin/cp -f $BL_FILE $BL_FILE_MAP > /dev/null 2>&1
MAP_CHANGED=1
fi
if ! diff $WL_FILE $WL_FILE.bak > /dev/null 2>&1; then
/bin/cp -f $WL_FILE_MAP $WL_FILE_MAP.bak > /dev/null 2>&1
/bin/cp -f $WL_FILE $WL_FILE_MAP > /dev/null 2>&1
MAP_CHANGED=1
fi
if [ "$MAP_CHANGED" -eq "1" -o "$UNBLOCK" -eq "1" ]; then
RELOAD=`/usr/sbin/nginx -t 2>&1 | /bin/grep ok`
if [ -n "$RELOAD" ];then
/sbin/service nginx reload
e "nginx is reloaded" >> $LOGFILE2
else
ERROR_RELOAD=`/sbin/service nginx configtest 2>&1`
/bin/cp -f $BL_FILE_MAP.bak $BL_FILE_MAP > /dev/null 2>&1
/bin/cp -f $WL_FILE_MAP.bak $WL_FILE_MAP > /dev/null 2>&1
e "nginx error config test failed" >> $LOGFILE2
fi
fi
fi
/bin/rm -rf $LOCK
Скрипт проверки частоты рефереров и формирование файла referer-block.conf вида:
~domain.ru 0;
~… 1;
~… 1;
Запускается ежеминутно.
#!/bin/bash
# referer_protect v.1.0.6
# Базовые настройки скрипта, обычно выносятся в отдельный файл,
# чтобы их можно было подстраивать под конкретный проект, не меняя основной скрипт.
RECORDS=500
DOMAIN_LIST=domain
LA=15 # if Load Average > $LA = Referer is block
BLOCK_TIME=360 #in minutes
#REF_WHITELIST=""
BLOCK_ENABLE=true # true/false - enable/disable add firewall rule.
email="mail@mail.ru"
LOGFILE=/var/log/referer-table.log
LOGFILE2=/var/log/referer-table-history.log
LOCK=/tmp/referer.lock
MSG_ALERT=/tmp/msg-alert.tmp
debug="0"
LA_CURRENT="`cat /proc/loadavg | awk '{ print $1}' | awk 'BEGIN { FS="."; }{ print $1}'`"
DT=`date "+%F %T"`
[ ! -f $LOGFILE ] && touch $LOGFILE
[ -f "$MSG_ALERT" ] && rm -f $MSG_ALERT
function e {
echo -e $(date "+%F %T") $1
}
function msg {
echo "Referer:$REFERER. Domain:$D" >> $MSG_ALERT
}
function send_mail {
if [ "$BLOCK_ENABLE" = "true" -a "$LA_CURRENT" -gt "$LA" ];then
cat $MSG_ALERT | mailx -s "Referers report. Warning" $email
elif [ "$BLOCK_ENABLE" = "true" -a "$LA_CURRENT" -le "$LA" ];then
cat $MSG_ALERT | mailx -s "Referers report. Notice " $email
else
cat $MSG_ALERT | mailx -s "Referers report. Notice (Test mode)" $email
fi
}
[ -f $LOCK ] && e "Script $0 is already runing" && exit
touch $LOCK
NEED_NGINX_RELOAD=0
for D in $DOMAIN_LIST
do
TMP_LOG=/tmp/ddos-$D-acc-referer.log
TMP_AWK=/tmp/tmp_$D-awk.tmp
#NGINX_LOG=/srv/www/$D/logs/$D-acc
NGINX_LOG=/srv/www/$D/shared/log/$D-acc.log
REFCONF=/etc/nginx/referer-block-$D.conf
[ ! -s "$REFCONF" ] && echo "~$D 0;" >> $REFCONF
if [ ! -f $NGINX_LOG ];then
echo "Log ($NGINX_LOG) not found."
/bin/rm -rf $LOCK
exit
fi
tail -10000 $NGINX_LOG | awk '($9 == "200") || ($9 == "404")' | awk '{print $11}' | sort | uniq -c | sort -n | awk -v x=$RECORDS ' $1 > x {print $2} ' > $TMP_LOG
sed -i "s/\"//g" $TMP_LOG # убираем кавычки
sed -i "/^-/d" $TMP_LOG # убираем referer "-"
sed -i "/$D/d" $TMP_LOG # убираем свой домен
sed -i "/^localhost/d" $TMP_LOG # убираем localhost
awk -F/ '{print $3}' $TMP_LOG > $TMP_AWK # оставляем только домен от url
cat $TMP_AWK > $TMP_LOG
# Разблокируем заблокированных referer
while read line
do
if [[ "$line" == *=* ]]; then
GET_TIME=`echo $line | awk -F"=" '{print $2}'`
NOW=`date +%s`
#echo $NOW
#echo $GET_TIME
if [ "$NOW" -gt "$GET_TIME" ]; then
REFERER=`echo $line | awk '{print $4}'`
e "Referer $REFERER unblocked." >> $LOGFILE2
/bin/sed -i '/'$REFERER'/d' $LOGFILE
/bin/sed -i '/'$REFERER'/d' $REFCONF
NEED_NGINX_RELOAD=1
fi
fi
done < $LOGFILE
# Блокируем referer
while read line
do
REFERER=$line
DOUBLE=`cat $REFCONF | grep "$REFERER"`
if [ -n "$DOUBLE" ]; then
[ "$debug" != "0" ] && e "referer $REFERER exist in DROP rule"
else
if [ "$BLOCK_ENABLE" = "true" -a "$LA_CURRENT" -gt "$LA" ];then
echo "~$REFERER 1;" >> $REFCONF
e "Referer $REFERER blocked $BLOCK_TIME minutes ($D) Unblock = `date --date="$BLOCK_TIME minute" +%s`" >> $LOGFILE
e "Referer $REFERER blocked $BLOCK_TIME minutes ($D)" >> $LOGFILE2
NEED_NGINX_RELOAD=1
if [ ! -s "$MSG_ALERT" ];then
echo "Status: WARNING" > $MSG_ALERT
echo "Date: $DT" >> $MSG_ALERT
echo "Referer: $RECORDS matches from 10000" >> $MSG_ALERT
echo "LA: $LA_CURRENT" >> $MSG_ALERT
echo "Referer(s) is blocked on $BLOCK_TIME minutes:" >> $MSG_ALERT
echo "" >> $MSG_ALERT
fi
msg
elif [ "$BLOCK_ENABLE" = "true" -a "$LA_CURRENT" -le "$LA" ];then
TESTDOUBLE=`cat $LOGFILE | grep "$REFERER"`
if [ -z "$TESTDOUBLE" ]; then
e "Referer $REFERER TEST blocked $BLOCK_TIME minutes ($D) Unblock = `date --date="$BLOCK_TIME minute" +%s`" >> $LOGFILE
e "TEST. Referer $REFERER TEST blocked $BLOCK_TIME minutes ($D)" >> $LOGFILE2
if [ ! -s "$MSG_ALERT" ];then
echo "Status: Notice" > $MSG_ALERT
echo "Date: $DT" >> $MSG_ALERT
echo "Referer: $RECORDS matches from 10000" >> $MSG_ALERT
echo "LA: $LA_CURRENT" >> $MSG_ALERT
echo "Referer(s) not blocking:" >> $MSG_ALERT
echo "" >> $MSG_ALERT
fi
msg
fi
else
TESTDOUBLE=`cat $LOGFILE | grep "$REFERER"`
if [ -z "$TESTDOUBLE" ]; then
e "Referer $REFERER TEST blocked $BLOCK_TIME minutes ($D) Unblock = `date --date="$BLOCK_TIME minute" +%s`" >> $LOGFILE
e "TEST. Referer $REFERER TEST blocked $BLOCK_TIME minutes ($D)" >> $LOGFILE2
if [ ! -s "$MSG_ALERT" ];then
echo "Date: $DT" > $MSG_ALERT
echo "Current referer found over $RECORDS matches from 10000 records, but script working is TEST MODE " >> $MSG_ALERT
echo "Current LA - $LA_CURRENT" >> $MSG_ALERT
echo "Referer(s) not blocking:" >> $MSG_ALERT
echo "" >> $MSG_ALERT
fi
msg
fi
fi
fi
done < $TMP_LOG
[ -n "email" -a -s "$MSG_ALERT" ] && send_mail
done
# reload nginx if config change
if [ $NEED_NGINX_RELOAD -eq 1 ]; then
/sbin/service nginx reload >/dev/null 2>/dev/null
fi
/bin/rm -rf $LOCK
Конфигурационный файл nginx как симлинк указывает на один из двух файлов, работающих в обычном и high LA режимах.
Режим переключается скриптом, исполняемым ежеминутно из Cron
#!/bin/bash
### check LA level
MAX_LA=10
processid=`/sbin/pidof -x $(basename $0) -o %PPID`
if [[ $processid ]];then
exit
fi
CFG_DDOS='fpm.domain.ru.ddos'
CFG_NODDOS='fpm.domain.ru.noddos'
load_average=$(uptime | awk '{print $11}' | cut -d "." -f 1)
echo "$(date '+%Y-%m-%d %H:%M') : LA $load_average"
if [[ $load_average -ge $MAX_LA ]]; then
if [ -f /tmp/la_flag ]; then
date '+%s' > /tmp/la_flag
exit 1
else
# echo "$(date +%Y-%m-%d-%H-%M)"
date '+%s' > /tmp/la_flag
mv /etc/nginx/vhosts.d/new.domain.ru.conf /etc/nginx/vhosts.d/new.domain.ru.conf.bak > /dev/null 2>&1
ln -s /etc/nginx/vhosts.d/$CFG_DDOS /etc/nginx/vhosts.d/new.domain.ru.conf
reload=`/usr/sbin/nginx -t 2>&1 | grep ok`
if [ -n "$reload" ];then
/sbin/service nginx reload
rm -f /etc/nginx/vhosts.d/new.domain.ru.conf.bak > /dev/null 2>&1
echo "$(date '+%Y-%m-%d %H:%M') : DDOS config up $reload"
exit 0
else
/sbin/service nginx configtest 2>&1
mv /etc/nginx/vhosts.d/new.domain.ru.conf.bak /etc/nginx/vhosts.d/new.domain.ru.conf > /dev/null 2>&1
echo "nginx error config ddos test failed"
echo "alarm nginx config ddos test failed" | mail -s alarm root
exit 1
fi
fi
else
if [ -f /tmp/la_flag ]; then
TIMEA=`cat /tmp/la_flag`
TIMEC=`date '+%s'`
TIMED=$(( $TIMEC - $TIMEA ))
if [ $TIMED -gt 600 ]; then
echo "high LA ENDED $(date +%Y-%m-%d-%H-%M)"
rm -f /tmp/la_flag > /dev/null 2>&1
mv /etc/nginx/vhosts.d/new.domain.ru.conf /etc/nginx/vhosts.d/new.domain.ru.conf.bak > /dev/null 2>&1
ln -s /etc/nginx/vhosts.d/$CFG_NODDOS /etc/nginx/vhosts.d/new.domain.ru.conf
reload=`/usr/sbin/nginx -t 2>&1 | grep ok`
echo "$(date '+%Y-%m-%d %H:%M') : NO DDOS config up $reload"
if [ -n "$reload" ];then
/sbin/service nginx reload
rm -f /etc/nginx/vhosts.d/new.domain.ru.conf.bak > /dev/null 2>&1
echo "$(date '+%Y-%m-%d %H:%M') : NO ddos config up"
exit 0
else
/sbin/service nginx configtest 2>&1
mv /etc/nginx/vhosts.d/new.domain.ru.conf.bak /etc/nginx/vhosts.d/new.domain.ru.conf > /dev/null 2>&1
echo "nginx error config noddos test failed"
echo "alarm nginx config noddos test failed" | mail -s alarm root
exit 1
fi
else
exit 1
fi
else
exit 1
fi
fi
Часть конфигурации вынесена в отдельный файл, подключаемый в основных конфигурационных файлах.
map_hash_bucket_size 128;
geoip_country /usr/share/GeoIP/GeoIP.dat;
limit_req_zone $newlimit_addres1 zone=newone:10m rate=50r/m;
map $whitelist-$remote_addr:$remote_port $newlimit_addres1 {
~"^0" $binary_remote_addr;
~"^1-(?<match_rap>.*)" $match_rap;
}
geo $whitelist {
default 0;
91.205.47.150 1;
194.87.91.154 1;
83.69.225.78 1;
77.88.18.82 1;
91.143.46.202 1;
213.180.192.0/19 1;
87.250.224.0/19 1;
77.88.0.0/18 1;
93.158.128.0/18 1;
95.108.128.0/17 1;
178.154.128.0/17 1;
199.36.240.0/22 1;
84.201.128.0/18 1;
141.8.128.0/18 1;
188.134.88.105 1;
89.163.3.25 1;
46.39.246.91 1;
84.21.76.123 1;
136.243.83.53 1;
77.50.238.152 1;
83.167.117.49 1;
109.188.82.40 1;
79.141.227.19 1;
176.192.62.78 1;
86.62.91.133 1;
144.76.88.101 1;
}
# блокировка рефереров через скрипт block_referer.sh
map $http_referer $bad_referer {
default "0";
include /etc/nginx/referer-block.conf;
}
map $http_referer:$request_method $bad_post_referer {
default "0";
"~*domain.ru.*:POST$" "0";
"~*:POST$" "1";
include /etc/nginx/referer-block.conf;
}
# Некоторый набор спицефичных блокировок проекта
map $query_string $bad_query {
...
default 0;
}
# проверка кук, которые устанавливают в файле /checkcapcha.php
map $http_cookie $allowed_cookie {
"~somecookie" 1;
default 0;
}
Ограничение по GeoIP в режиме Под атакой
map $geoip_country_code $allowed_country {
RU 1;
default 0;
}
# блокировка подсетей Amazon
include vhosts.d/deny-amazon.inc;
# Ручные белый и черный списки
map $remote_addr $valid_addr {
include vhosts.d/main_blacklist.map;
include vhosts.d/main_whitelist.map;
default 2;
}
# UA посетителей-ботов
map $http_user_agent $user_agent_search_bot {
"~Yandex" "1";
"~Google" "1";
"~*bing" "1";
"~*MSNBot" "1";
default "";
}
map $remote_addr $ptr_wl_bl {
include vhosts.d/ptr_blacklist.map;
include vhosts.d/ptr_whitelist.map;
default "";
}
map "$user_agent_search_bot:$ptr_wl_bl" $searchbot {
"1:1" "1";
"1:0" "0";
default "2";
}
Листинги конфигурационных файлов
include vhosts.d/map.domain.ru.inc;
map "$searchbot:$valid_addr:$bad_referer:$bad_query" $root_location_p1 {
default @allow_limit;
"~^1:" @allow;
"~^2:1" @allow_limit;
"~^0" @loc_403;
"~^2:0" @loc_403;
"2:2:1:0" @loc_403;
"2:2:1:1" @loc_403;
"2:2:0:1" @loc_403;
}
map "$searchbot:$valid_addr:$bad_post_referer:$bad_query" $root_only_location_p1 {
default @allow_limit;
"~^1:" @allow;
"~^2:1" @allow_limit;
"~^0" @loc_403;
"~^2:0" @loc_403;
"2:2:1:0" @loc_403;
"2:2:1:1" @loc_403;
"2:2:0:1" @loc_403;
}
########################################################
server {
listen 80;
listen 443 ssl;
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_connect_timeout 300s;
server_name domain.ru www.domain.ru m.domain.ru www.m.domain.ru;
ssl_certificate ssl/www.domain.ru.crt;
ssl_certificate_key ssl/www.domain.ru.key;
charset UTF-8;
access_log /srv/www/domain/shared/log/domain-acc.log main;
error_log /srv/www/domain/shared/log/domain-err.log;
root /srv/www/domain/current/public/;
error_page 500 502 /highla.html;
# Выдается capcha в фарме POST с action="/checkcapcha.php"
location = /highla.html {
charset UTF-8;
root /srv/www/domain/current/public/;
allow all;
}
# Устанавливается хэшированная кука на базе адреса посетителя.
location = /checkcapcha.php {
charset UTF-8;
root /srv/www/domain/current/public/;
include fastcgi_params;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_pass 127.0.0.1:9000;
allow all;
}
# Именованные location для ветвления по ним через переменную map
location @loc_403 {
access_log /srv/www/domain/shared/log/loc_403-acc main;
return 403;
}
location @allow {
access_log /srv/www/domain/shared/log/allow-acc main;
add_header X-debug-message "Allow";
try_files $uri /index.php?$query_string;
}
location @allow_limit {
limit_req zone=newone burst=15;
access_log /srv/www/domain/shared/log/allow-acc main;
add_header X-debug-message "Allow";
try_files $uri /index.php?$query_string;
}
location @deny {
access_log /srv/www/domain/shared/log/deny-acc main;
add_header X-debug-message "Deny";
return 403;
}
location @restrict {
access_log /srv/www/domain/shared/log/resrtict-acc main;
add_header X-debug-message "Restrict";
return 502;
}
location / {
try_files /fake-nonexistens-location-forr273 $root_location_p1;
}
location = / {
try_files /fake-nonexistens-location-forr273 $root_only_location_p1;
}
location ~* \.php {
include fastcgi_params;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_pass 127.0.0.1:9000;
}
location ~* \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|tar|mid|midi|wav|bmp|rtf|js|swf|flv|avi|djvu|mp3)$ {
root /srv/www/domain/current/public;
expires 7d;
access_log off;
log_not_found off;
}
location ~ /\.git {
deny all;
}
location ~ /\.ht {
deny all;
}
location ~ /\.svn {
deny all;
}
}
include vhosts.d/map.domain.ru.inc;
map "$searchbot:$valid_addr:$bad_referer:$bad_query" $root_location {
default @main;
"~^1:" @allow;
"~^2:1" @allow;
"~^0" @loc_403;
"~^2:0" @loc_403;
"2:2:1:0" @loc_403;
"2:2:1:1" @loc_403;
"2:2:0:1" @loc_403;
}
map "$searchbot:$valid_addr:$bad_post_referer:$bad_query" $root_only_location {
default @main;
"~^1:" @allow;
"~^2:1" @allow;
"~^0" @loc_403;
"~^2:0" @loc_403;
"2:2:1:0" @loc_403;
"2:2:1:1" @loc_403;
"2:2:0:1" @loc_403;
}
map "$allowed_country:$allowed_cookie" $main_location {
"1:0" @allow_limit;
"1:1" @allow_limit;
"0:1" @allow_limit;
default @restrict;
}
########################################################
server {
listen 80;
listen 443 ssl;
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_connect_timeout 300s;
server_name domain.ru www.domain.ru m.domain.ru www.m.domain.ru;
ssl_certificate ssl/www.domain.ru.crt;
ssl_certificate_key ssl/www.domain.ru.key;
charset UTF-8;
access_log /srv/www/domain/shared/log/domain-acc.log main;
error_log /srv/www/domain/shared/log/domain-err.log;
root /srv/www/domain/current/public/;
# Выдается capcha в фарме POST с action="/checkcapcha.php"
error_page 500 502 /highla.html;
location = /highla.html {
charset UTF-8;
root /srv/www/domain/current/public/;
allow all;
}
# Устанавливается хэшированная кука на базе адреса посетителя.
location = /checkcapcha.php {
charset UTF-8;
root /srv/www/domain/current/public/;
include fastcgi_params;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_pass 127.0.0.1:9000;
allow all;
}
# Именованные location для ветвления по ним через переменную map
location @loc_403 {
access_log /srv/www/domain/shared/log/loc_403-acc main;
return 403;
}
location @allow {
access_log /srv/www/domain/shared/log/allow-acc main;
add_header X-debug-message "Allow";
try_files $uri /index.php?$query_string;
}
location @allow_limit {
limit_req zone=newone burst=55;
access_log /srv/www/domain/shared/log/allow-limit-acc main;
add_header X-debug-message "Allow";
try_files $uri /index.php?$query_string;
}
location @deny {
access_log /srv/www/domain/shared/log/deny-acc main;
add_header X-debug-message "Deny";
return 403;
}
location @restrict {
access_log /srv/www/domain/shared/log/resrtict-acc main;
add_header X-debug-message "Restrict";
return 502;
}
location @main {
add_header X-debug-message "Main";
try_files /fake-nonexistens-location-forr273 $main_location;
}
location / {
try_files /fake-nonexistens-location-forr273 $root_location;
}
location = / {
try_files /fake-nonexistens-location-forr273 $root_only_location;
}
location ~* \.php {
include fastcgi_params;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_pass 127.0.0.1:9000;
}
location ~* \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|tar|mid|midi|wav|bmp|rtf|js|swf|flv|avi|djvu|mp3)$ {
root /srv/www/domain/current/public;
expires 7d;
access_log off;
log_not_found off;
}
location ~ /\.git {
deny all;
}
location ~ /\.ht {
deny all;
}
location ~ /\.svn {
deny all;
}
}
Итог
Этим решением мы помогли нашему клиенту защитить свой проект от паразитного трафика и повысить стабильность работы серверов.
Автор: ведущий системный администратор компании Марат Рахимов.
Комментарии (54)
gearbox
04.03.2016 12:55+20у паразитного трафика имеются определенные паттерны поведения и свойства
Проблема в том что вы обнаруживаете свойства, заранее вам известные. Попробуйте положить в свои страницы код проверки на наличие пользователя (типа рекапчи от гугля, только не капча а именно проверка на наличие действий пользователя — метание курсора, выделение текста) — и удивитесь сколько вас фантомом парсят, и при этом в озвученные выше фильтры не попадают.
А проверка капчей — честно, спорно. Что бы отсечь посещения которые мешают только вам, вы создаете неудобства своим пользователям. За что?maxru
04.03.2016 14:28+7Можно написать webdriver-бота (хотя бы и через selenium), он и мышкой будет дёргать и выделять текст и вообще.
(зато будут забанены пользователи, которые жабаскрипт отключили)gearbox
04.03.2016 15:02+1можно. Но тут уже для качественной имитации надо иметь хорошую стату что бы выделить паттерны поведения пользователей и имитировать их деятельность. не скажу что нет таких в природе, но это уже игроки другого эшелона.
Да и в общем то моя реплика больше была не о том что авторы делают что то неправильно а скорее о том что этого вообще делать то и не надо (на мой взгляд, допускаю что он может быть ошибочным).tbicr
04.03.2016 16:29-1В общем цена получения данных или содания нагрузки противостоит цене защиты от них с сохранением прибыли.
maxru
10.03.2016 13:20Авторы в любом случае выделили один паттерн из миллиона и используют его.
Тут вопрос в том, сколько %% ботов они отсекают этим паттерном.
naryl
04.03.2016 15:07+6У меня джаваскрипт отключён, а когда включён, я ни мышкой не метаюсь, ни текст не выделяю, т.к. Vimperator.
UPD: И да, писал парсеры, ни один под приведённый в статье список поведения не попадает, кроме пустого referer'а. Это просто, чтобы подтвердить первую половину Вашей точки зрения.: )
UPD2: А *большинство* запросов к разделу товаров скорее всего у вообще всех посетителей сайта, если это магазин.
khim
04.03.2016 16:36+9У меня такое ощущения сложилось, что задачи "отшить всех ботов" вообще не ставилось.
Об этом ещё из преамбулы видно: ну стащат что-то боты и стащат, не в этом дело. Хотелось, как я понял, "пришибить" ботов создающих повышенную нагрузку и при этом не дающих ничего взамен (поисковики, понятно, дают и понятно что).
А CAPTCHA — это, как я понял, от ложных срабатываний: если человека вдруг записали в боты — он разгадает картинку и его пропустят, а для ботоводов есть более простые решения — просто переписать их так, чтобы они не создавали излишней нагрузки...postgree
04.03.2016 20:02+1А для создателей интернетов тогда нужно разработать стандарт файлов, в котором будет лежать вся информация для парсящих. Разрабатывал и парсеры, и системы, которые определяют ботов. Защищаться от ботов себе дороже. Проще переформировывать файл с каталогом товаров (прайс лист).
merlin-vrn
04.03.2016 20:11+3Я тоже подумал об этом: ну господи, грузят они, нужна им эта информация, ну так дайте им её простым способом, чтобы им не приходилось парсить. Или даже продайте занедорого.
Проблема в том, что многие магазины по разным причинам очень не хотят сравнений с конкурентами, а если выдать свой прайс, да ещё в стандартном машиночитаемом формате — сравнения будут на каждом шагу. Поэтому они не захотят делать свои прайсы в таком формате, и парсинг запрещают.sebres
05.03.2016 01:43для ботоводов с бот-сеткой — фигня вопрос… скажем есть у него в наличии 500 уникальных IP, при сотне запросов в сутки с каждого, ежедневно у него в наличии актуальный прайс лист на 50.000 артиклей… и у конкурентов цена на пару процентов ниже.
При том, что свои зоопарки ботоводы часто разводят на компах "домохозяек" и т.п. — все эти ip ещё и динамические в добавок… т.е. завтра другие как правило...
vit1251
04.03.2016 13:12+5И это все компания делает во время развития семантической концепции интернета.
Возможно нужно развивать просто защиту информации как-то подписывать контент.
Или регистрировать себя как первоисточник в тех же поисковых системах.
vlreshet
04.03.2016 14:41+3ИМХО, это защита только от каких-нибудь общих парсеров, которые не заточены сугубо на ваш сайт. Если у кого-то есть цель парсить именно вас — ничем от этого не защититься
jehy
04.03.2016 15:47+24Очень российский подход — не знаем, кто и зачем к нам ходит, но давайте на всякий случай запретим. Наверняка это злодеи. Картинка к посту это эпично подтверждает.
Это же могли быть боты людей, которые занимаются перепродажей товаров вашего клиента, например. Они же не из вредности к вам приходят, а с тем, чтобы сделать что-то полезное для себя и возможно для вас. То, что всплеск наблюдался ежедневно в одно и то же время и на категории товаров это подтверждает — у вас не искали дыры, не ломали вас, не пытались перегрузить сервер — просто тихо-мирно читали информацию.
Вы могли бы просто поставить лимит на количество запросов с IP адреса в единицу времени — и клиент был бы доволен, и ботоводы бы перенастроили своих ботов, чтобы они аккуратнее ходили на ваш сайт.
А совсем правильно — ещё вывесить большое объявление о том, что готовы к различного рода партнёрской программе и выгружать данные любым желающим.
В общем, не удивляйтесь, если у клиента внезапно упадут продажи. Нет, боты не мои, если что.akhaustov
04.03.2016 15:56-7Данная система была согласована с заказчиком и внедрялась по его же просьбе.
Причиной был постоянный парсинг проекта, что давало бесполезную нагрузку.
Установка лимитов не давала нужного эффекта.
Конечно же, никто не будет совершать действий в ущерб бизнесу.
Считаю, что Ваш комментарий совершенно не в тему.jehy
04.03.2016 16:12+11Комментарий — к тому, что утверждение о том, что нагрузка бесполезна и трафик "паразитарный" — крайне необоснованно.
Например, вас могут сканировать бот, который предлагает товары по самым низким ценам. В результате бот не сможет парсить ваш сайт, и даже если он продаёт что-то очень хорошее по низкой цене — вы просто вывалитесь из рейтинга.
Ну и с архитектурной точки зрения решения просто ужасно. Хотя бы по причине постоянной перезагрузки настроек nginx. Когда вы запускаете какой-то процесс сервера (в особенности веб сервер или СУБД), то ожидаете, что он будет работать с изначальными настройками всё время до ручной перезагрузки или ребута. А выходит, что если некий администратор правил настройки — например, хотел подключить SSL — и не доделал их, сохранив в уверенности что вернётся к этому чуть позже, то неверные настройки будут автоматически использованы.
Если уж заказчик захотел такой функционал, то было правильно использовать подходящие для него средства — например, fail2ban. По айпи адерсам и юзерагентам он отлично умеет определять ботов. Можно было бы так же дописать некие свои проверки — например, сделанную вам проверку по PTR… Хотя кажется, fail2ban умеет и это. Нет, сделали какой-то ужасный костыль и довольны.akhaustov
04.03.2016 16:23Тут именно по настоянию заказчика занимались реализацией этой задачи. Согласен, что это костыль. fail2ban не помог. Возможно, стоило просто стать под защиту специализированных сервисов. Однако, заказчик настоял именно на таком вот решении. Поверьте, пытались переубедить :)
jehy
04.03.2016 16:34А что не срослось с fail2ban? Верю, что могли быть какие-то интересные грабли, интересно узнать, какие. Сам я fail2ban использовал для защиты ftp/smtp и ещё нескольких сервисов, с http пока не было потребности — но интернет говорит, что используют успешно.
sebres
04.03.2016 21:49+9fail2ban не помог
Ну как бы в мире ИТ не существует подобных словосочетаний — "не работает", "не помогло" (без конкретики).
А как разработчик fail2ban пока поверю только в то, что установив его (из коробки), вы не получили требуемого результата. Имхо, вполне ожидаемо и закономерно (ибо нужно его "готовить", в принципе как и любой инструмент).
Точно так-же вы могли бы установить что-нибудь типа spamassassin, и не поправив ни одного конфига, без биндингов (вообще ничего не настраивая), удивляться тому, что до сих пор спам сыплется.
В результате (вероятно просто не разобравшись) вы сделали велосипед, плохой-хороший не суть важно, ибо все-равно велик. Потому что, если я правильно разобрал, что вы "создали", то как минимум половину работы (если не 2/3) можно было переложить на f2b...
merlin-vrn
04.03.2016 20:13+2А вы пытались хотя бы намекнуть клиенту, что, возможно, стоит вместо того, чтобы бороться — возглавить? Сделать api, чтобы желающие мгли получить данные не грузя сайт, без всякого парсинга?
verydima
05.03.2016 01:02Всё очень правильно написано. Я занимаюсь парсингом более трёх лет. И за 3 года я видел только один сайт, который нельзя было спарсить. Точнее, в заданном промежутке времени я не нашёл решения. Для каждого сайта можно сделать парсер. И он будет эммулировать всё то, что Вы описали в статье и даже больше.
Наиболее рациональная защита — это ограничение по количеству запросов с 1 ip в единицу времени. Большинство живых людей не будут сидеть и листать Ваш сайт час страницу за страницей. Либо же отдача какой-то части материала. Например, если человек ищет какую-то услугу в каком-то регионе, то ему отдаются не все 40000 результатов поиска, а 3000 первых записей. Для живого человека этого более, чем достаточно (именно так работает таже manta.com).khim
05.03.2016 04:40+7Большинство живых людей не будут сидеть и листать Ваш сайт час страницу за страницей.
Зато за одним proxy-IP может оказаться вдруг несколько тысяч (если не миллионов) живых людей.
antonwork
04.03.2016 16:34-5Сколько эти парсеры давали нагрузки, что "LA значительно рос на серверах". 100 запросов в секунду? 1000? Может проще было оптимизировать код, пересмотреть какие-то подходы, чтобы просто не замечать эту избыточную нагрузку? Ну а если с одного адреса проходит запредельное кол-во запросов на бэкенд мимо кешей, то можно поместить этот адрес в некий список, которому показываю только капчу. (У вас что-то подобное уже реализовано)
Удивительно, я конечно не знаю всех деталей, но столько вложить сил в латание… по факту костылей.
leMar
04.03.2016 16:35В кои-то веки нормальная техническая статья про конфигурирование, а все комментарии про надо / не надо и не одного про конфиги. На хабре админы остались, вообще?
jehy
04.03.2016 16:38+3Чуть выше автор признаёт, что это специфический костыль, обусловленный требованиями заказчика. Ну и не совсем понятно, какие комментарии про конфиги вы хотите услышать.
FeNUMe
04.03.2016 21:08+10В статье описан жуткий костыль и она скорее является примером как делать не надо, потому и реакция соответствующая.
questor
04.03.2016 17:02+2Статья и тематика спорная, но прочитал с интересом. Не хватает цифр — доля паразитного трафика до и после, какие меры из реализованных больше всего повлияли на итоговую фильтрацию.
rekby
04.03.2016 18:24PTR-записи у многих хостеров ставятся автоматом любые и никто не мешает им поставить ptr-записи google или яндекс и т.п. Поисковики рекомендуют потом еще обратное преобразование проверять.
Еще может иметь смысл проверять IP-адреса по whois, тогда если whois правильный и соответствует яндексу/гуглу и т.п. — можно в белый список сразу всю их сеть внести и данные в whois подделать сложнее, чем PTR.
simpleadmin
04.03.2016 18:40+2geo $whitelist {
default 0;
…
77.88.0.0/18 1;
93.158.128.0/18 1;
95.108.128.0/17 1;
178.154.128.0/17 1;
199.36.240.0/22 1;
Не доверяйте просто так подсетям ПС. Они далеко не безгрешны. Парсинг 2-х летней давности с серверов Яндекса:
178.154.243.106 - 5011415253 1419741951.254 [GET] [/servis-centr/texet?ah62] 200 15241 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 178.154.243.105 - 5011428078 1419741951.345 [GET] [/servis-centr/texet?ah141] 200 241 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 178.154.243.104 - 5011415952 1419741951.354 [GET] [/servis-centr/texet?ah88] 200 19249 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 95.108.158.134 - 5011424897 1419741951.430 [GET] [/servis-centr/texet?ah69] 200 52415 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 178.154.243.107 - 5011410458 1419741951.574 [GET] [/remont-chasov/royal-london18ao] 200 8029 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 5.255.253.3 - 5011428792 1419741951.586 [GET] [/remont-chasov/royal-london19au] 200 8030 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 95.108.128.241 - 5011423471 1419741951.593 [GET] [/remont-chasov/royal-london22a] 200 8028 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 [] 95.108.128.242 - 5011427425 1419741951.617 [GET] [/servis-centr/texet?ah73] 200 15245 - [Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5409 Safari/536.5] m*****.ru XXX.XXX.217.215:80 28/Dec/2014:07:45:51 +0300 RU w:1 []
Около 3'000'000 запросов за сутки. Разумеется никакими Яндекс-ботами здесь и не пахнет
reji
04.03.2016 19:42+2А что мешало реализовать динамическую роботоловилку с прослойкой перед внутри nginx на lua? Так делает, например, Wallarm.
По временным затратам вышло бы примерно тоже самое, но скорость и качество работы ни в сравнение.
Не критики для, а вопроса ради. Решение, как я понимаю, уже протестировано и работает в рамках ТЗ заказчика. Интересует, почему вы пришли именно к такому решению.
P.S. Соглашусь с комментарием jehy, что можно было не банить, а попытаться связаться с владельцами ботов. Отдавая хотя бы стандартную страницу "call me maybe"
BigD
04.03.2016 21:37+1А мы боремся с теми, кто борется с нашими парсерами… Пока получается. Но мы не такие плохие (даже хорошие я бы сказал) — мы стараемся раскидывать нагрузку (запросы к сайту) в течение дня, а не разово пытаться скачать "весь Интернет".
Melz
04.03.2016 21:54Я иногда пишу парсеры как хобби. Даже у меня есть небольшие наработки по проходу сайтов и ваша защита так себе )
Лучше сделайте API, если вам кому-то нужны ваши продукты то пускай на них смотрят.
Если большой пик с одних адресов, то скорее всего идет «синхронизация», т.е. если бот не работал нужно добрать. А так инкремент как правило идет довольно быстро.
Когда парсерщим это надоесть они купят симку и модем и будут ходить на мобильную версию сайта с сети МТС и тд.
Мобильная версия накладывает ограничения на всякие капчи, а если забаните ip большой тройки сами плакать будете )
amaksr
04.03.2016 22:37+2Мне кажется странным, что интернет-магазин решил ограничить распространение данных из своего раздела Товаров (читай каталогов и прайс-листов). Обычно поступают наоборот. Может лучше было организовать API, а при детектировании бота выдавать ему подсказку с адресом этого API, чтобы облегчить жизнь админу этого бота?
nikitasius
04.03.2016 22:51+5Я, чесно, не пойму, какую такую ужасную нагрузку несет nginx динамике c, хотя бы, 30 минутным кешем каталога товаров.
Ну ей богу, не пойму. При правильной настройке ключей (с cookies и без cookies) у вас отдельные кеши для всех и для зарегистрированных клиентов/редакторов.
И в таком случае, как правильно заметили выше, роляет рейт запросов на IP. Если динамика тупая и кривая и работает на калькуяторе.\
Подходит только под: не хочу делиться, хочу быть уникальным! Глупости это все и трата времени. Кто хочет — спарсит. Что не может — попросит того, что может. Я сам, когда парсил нужные мне ресурсы представлялся или хромом, или гуглом, всегда отправлял реферера и еще пачку заголовков, иногда и кукисы поддерживал, и все в 5-6 потоков. Иногда часть через прокси. Мде.postgree
05.03.2016 03:15+2Потому что современные интернет магазины не кешируют ничего даже для анонимов/гостей. Необходим анализ переходов пользователя для отображения спец предложений, похожих или возможно заинтересующих товаров. Маркетологи на крупных проектах сейчас пытаются каждые вероятные 0,5 процента выжать.
Подходит только под: не хочу делиться, хочу быть уникальным! Глупости это все и трата времени. Кто хочет — спарсит. Что не может — попросит того, что может. Я сам, когда парсил нужные мне ресурсы представлялся или хромом, или гуглом, всегда отправлял реферера и еще пачку заголовков, иногда и кукисы поддерживал, и все в 5-6 потоков. Иногда часть через прокси. Мде.
Вас можно вычислить. На раз — два. Приходилось использовать вебдрайвер (selenium, PhantomJS), правильно водить мышкой, изучать поведенческие шаблоны и пр. Покупать распознавание капчи наконец. А с учетом стоимости таких услуг иногда только 80$ в день улетало на распознавание капчи.Blumfontein
05.03.2016 07:35+1>> Потому что современные интернет магазины не кешируют ничего даже для анонимов/гостей. Необходим анализ переходов пользователя для отображения спец предложений, похожих или возможно заинтересующих товаров. Маркетологи на крупных проектах сейчас пытаются каждые вероятные 0,5 процента выжать.
Это все можно на джаваскрипте асинхронно сделать.
merlin-vrn
05.03.2016 11:06+2Не на раз-два. Стоимость обнаружения качественного парсера сравнима со стоимостью разработки такого парсера. Короче, тут типичная борьба оружия и брони.
Только куча комментов в топике намекает, что это глупая борьба, особенно — в данном случае.
nikitasius
05.03.2016 23:20Потому что современные интернет магазины не кешируют ничего даже для анонимов/гостей
Вы их на мух и котлет разделяете при помощи cookies? 10 секундный кеш по ключу с кукисами решит вашу проблему. Просто добавить $cookiename к ключу.
Даже для тех, кто не слушает советов, 1 секундный кеш и рейты в каждом случаю 100% решают задачу и коллосально снижают нагрузки. А всю обработку нужно повесить на javascript, как писали ниже. Ну или покупать железо мощнее.
Вас можно вычислить. На раз — два.
У вас есть такой сайт (или вы с ним сталкивались), который вычисляет таких парсеров на раз-два? Дайте ссылку в личку, я хочу посмотреть.
torrie
05.03.2016 08:34-1Ой, ладно, какая нагрузка? Хотите с ней бороться — помогите парсерам, сделайте api. Или кешируйте данные. Или… Средств борьбы с нагрузкой куча!
Мне кажется, что вы просто боитесь за данные.
avonar
05.03.2016 12:28+1На что только не пойдут люди, лиж бы не сделать файлик с каталогом, в котором все будет лежать в удобоваримой форме.
Это какая-то борьба с ветряными мельницами, тем более что боты просто парсили контент.
Впрочем, типично российский подход к решению проблем, все запретить, никого не пускать, пускай клиенты монитор разобьют из-за капчей.
Shablonarium
05.03.2016 15:27Ну да, вместо того чтобы сделать удобный АПИ, давайте объявим потребителей злоумышленниками! Совковый маразм!
1it
Т.е. получается что таки каждую минуту может происходить nginx -s reload?
Мне кажется можно делегировать половину действий самому nginx и обойтись без частых reload.
akhaustov
Потенциально, может. На практике с таким не сталкивались пока. Да, и решение не претендует на универсальность. Для конкретного проекта хорошо подошло,