company_banner

Как мы боролись с парсерами

    image
    Ключевые моменты:
    * Реализация скрипта для проверки 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.

    Запускается ежеминутно.
    Листинг скрипта проверки соответствия UA и PTR:
    #!/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 
    


    Часть конфигурации вынесена в отдельный файл, подключаемый в основных конфигурационных файлах.

    Файл с общими параметрами конфигурации для обоих режимов vhosts.d/map.domain.ru.inc:
    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";
    }
    


    Листинги конфигурационных файлов

    Основной конфиг для нормального режима работы vhosts.d/fpm.domain.ru.noddos:
    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;
        } 
    }
    


    Конфиг для режима high LA vhosts.d/fpm.domain.ru.ddos:
    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;
        }
    }
    


    Итог

    Этим решением мы помогли нашему клиенту защитить свой проект от паразитного трафика и повысить стабильность работы серверов.
    Автор: ведущий системный администратор компании Марат Рахимов.
    Southbridge
    Обеспечиваем стабильную работу highload-проектов

    Comments 59

      +1
      Т.е. получается что таки каждую минуту может происходить nginx -s reload?
      Мне кажется можно делегировать половину действий самому nginx и обойтись без частых reload.
        0
        Потенциально, может. На практике с таким не сталкивались пока. Да, и решение не претендует на универсальность. Для конкретного проекта хорошо подошло,
        +20
        у паразитного трафика имеются определенные паттерны поведения и свойства

        Проблема в том что вы обнаруживаете свойства, заранее вам известные. Попробуйте положить в свои страницы код проверки на наличие пользователя (типа рекапчи от гугля, только не капча а именно проверка на наличие действий пользователя — метание курсора, выделение текста) — и удивитесь сколько вас  фантомом парсят, и при этом в озвученные выше фильтры не попадают.
        А проверка капчей — честно, спорно. Что бы отсечь посещения которые мешают только вам, вы создаете неудобства своим пользователям. За что?
          +7
          Можно написать webdriver-бота (хотя бы и через selenium), он и мышкой будет дёргать и выделять текст и вообще.
          (зато будут забанены пользователи, которые жабаскрипт отключили)
            +1
            можно. Но тут уже для качественной имитации надо иметь хорошую стату что бы выделить паттерны поведения пользователей и имитировать их деятельность. не скажу что нет таких в природе, но это уже игроки другого эшелона.
            Да и в общем то моя реплика больше была не о том что авторы делают что то неправильно а скорее о том что этого вообще делать то и не надо (на мой взгляд, допускаю что он может быть ошибочным).
              –1
              В общем цена получения данных или содания нагрузки противостоит цене защиты от них с сохранением прибыли.
                0
                Авторы в любом случае выделили один паттерн из миллиона и используют его.
                Тут вопрос в том, сколько %% ботов они отсекают этим паттерном.
              +6
              У меня джаваскрипт отключён, а когда включён, я ни мышкой не метаюсь, ни текст не выделяю, т.к. Vimperator.

              UPD: И да, писал парсеры, ни один под приведённый в статье список поведения не попадает, кроме пустого referer'а. Это просто, чтобы подтвердить первую половину Вашей точки зрения.: )

              UPD2: А *большинство* запросов к разделу товаров скорее всего у вообще всех посетителей сайта, если это магазин.
                +9
                У меня такое ощущения сложилось, что задачи "отшить всех ботов" вообще не ставилось.

                Об этом ещё из преамбулы видно: ну стащат что-то боты и стащат, не в этом дело. Хотелось, как я понял, "пришибить" ботов создающих повышенную нагрузку и при этом не дающих ничего взамен (поисковики, понятно, дают и понятно что).

                А CAPTCHA — это, как я понял, от ложных срабатываний: если человека вдруг записали в боты — он разгадает картинку и его пропустят, а для ботоводов есть более простые решения — просто переписать их так, чтобы они не создавали излишней нагрузки...
                  +2
                  В точку! :)
                    +1
                    А для создателей интернетов тогда нужно разработать стандарт файлов, в котором будет лежать вся информация для парсящих. Разрабатывал и парсеры, и системы, которые определяют ботов. Защищаться от ботов себе дороже. Проще переформировывать файл с каталогом товаров (прайс лист).
                      +3
                      Я тоже подумал об этом: ну господи, грузят они, нужна им эта информация, ну так дайте им её простым способом, чтобы им не приходилось парсить. Или даже продайте занедорого.

                      Проблема в том, что многие магазины по разным причинам очень не хотят сравнений с конкурентами, а если выдать свой прайс, да ещё в стандартном машиночитаемом формате — сравнения будут на каждом шагу. Поэтому они не захотят делать свои прайсы в таком формате, и парсинг запрещают.
                        0
                        для ботоводов с бот-сеткой — фигня вопрос… скажем есть у него в наличии 500 уникальных IP, при сотне запросов в сутки с каждого, ежедневно у него в наличии актуальный прайс лист на 50.000 артиклей… и у конкурентов цена на пару процентов ниже.
                        При том, что свои зоопарки ботоводы часто разводят на компах "домохозяек" и т.п. — все эти ip ещё и динамические в добавок… т.е. завтра другие как правило...
                        0
                        А для создателей интернетов тогда нужно разработать стандарт файлов,
                        А чем вам RSS не угодил?
                    +5
                    И это все компания делает во время развития семантической концепции интернета.
                    Возможно нужно развивать просто защиту информации как-то подписывать контент.
                    Или регистрировать себя как первоисточник в тех же поисковых системах.
                      +1
                      Вы получили временную передышку? Или смогли отвадить "парсеры" совсем?
                        0
                        От данного клиента отстали. Больше нигде не пробовали применять.
                        +3
                        ИМХО, это защита только от каких-нибудь общих парсеров, которые не заточены сугубо на ваш сайт. Если у кого-то есть цель парсить именно вас — ничем от этого не защититься
                          +6
                          У них теперь даже есть подробная инструкция по обходу защиты :)
                            0
                            Нужно только знать, что за клиент ) Из более, чем полутора сотен — сложно угадать.
                              +5
                              Если автор парсера читает хабр, ему угадывать не надо :)
                          +24
                          Очень российский подход — не знаем, кто и зачем к нам ходит, но давайте на всякий случай запретим. Наверняка это злодеи. Картинка к посту это эпично подтверждает.

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

                          Вы могли бы просто поставить лимит на количество запросов с IP адреса в единицу времени — и клиент был бы доволен, и ботоводы бы перенастроили своих ботов, чтобы они аккуратнее ходили на ваш сайт.

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

                          В общем, не удивляйтесь, если у клиента внезапно упадут продажи. Нет, боты не мои, если что.
                            –7
                            Данная система была согласована с заказчиком и внедрялась по его же просьбе.
                            Причиной был постоянный парсинг проекта, что давало бесполезную нагрузку.
                            Установка лимитов не давала нужного эффекта.
                            Конечно же, никто не будет совершать действий в ущерб бизнесу.
                            Считаю, что Ваш комментарий совершенно не в тему.
                              +11
                              Комментарий — к тому, что утверждение о том, что нагрузка бесполезна и трафик "паразитарный" — крайне необоснованно.
                              Например, вас могут сканировать бот, который предлагает товары по самым низким ценам. В результате бот не сможет парсить ваш сайт, и даже если он продаёт что-то очень хорошее по низкой цене — вы просто вывалитесь из рейтинга.

                              Ну и с архитектурной точки зрения решения просто ужасно. Хотя бы по причине постоянной перезагрузки настроек nginx. Когда вы запускаете какой-то процесс сервера (в особенности веб сервер или СУБД), то ожидаете, что он будет работать с изначальными настройками всё время до ручной перезагрузки или ребута. А выходит, что если некий администратор правил настройки — например, хотел подключить SSL — и не доделал их, сохранив в уверенности что вернётся к этому чуть позже, то неверные настройки будут автоматически использованы.

                              Если уж заказчик захотел такой функционал, то было правильно использовать подходящие для него средства — например, fail2ban. По айпи адерсам и юзерагентам он отлично умеет определять ботов. Можно было бы так же дописать некие свои проверки — например, сделанную вам проверку по PTR… Хотя кажется, fail2ban умеет и это. Нет, сделали какой-то ужасный костыль и довольны.
                                0
                                Тут именно по настоянию заказчика занимались реализацией этой задачи. Согласен, что это костыль. fail2ban не помог. Возможно, стоило просто стать под защиту специализированных сервисов. Однако, заказчик настоял именно на таком вот решении. Поверьте, пытались переубедить :)
                                  0
                                  А что не срослось с fail2ban? Верю, что могли быть какие-то интересные грабли, интересно узнать, какие. Сам я fail2ban использовал для защиты ftp/smtp и ещё нескольких сервисов, с http пока не было потребности — но интернет говорит, что используют успешно.
                                    +9
                                    fail2ban не помог

                                    Ну как бы в мире ИТ не существует подобных словосочетаний — "не работает", "не помогло" (без конкретики).

                                    А как разработчик fail2ban пока поверю только в то, что установив его (из коробки), вы не получили требуемого результата. Имхо, вполне ожидаемо и закономерно (ибо нужно его "готовить", в принципе как и любой инструмент).

                                    Точно так-же вы могли бы установить что-нибудь типа spamassassin, и не поправив ни одного конфига, без биндингов (вообще ничего не настраивая), удивляться тому, что до сих пор спам сыплется.

                                    В результате (вероятно просто не разобравшись) вы сделали велосипед, плохой-хороший не суть важно, ибо все-равно велик. Потому что, если я правильно разобрал, что вы "создали", то как минимум половину работы (если не 2/3) можно было переложить на f2b...
                                  +2
                                  А вы пытались хотя бы намекнуть клиенту, что, возможно, стоит вместо того, чтобы бороться — возглавить? Сделать api, чтобы желающие мгли получить данные не грузя сайт, без всякого парсинга?
                                    –1
                                    вот-вот. нас просто блокировали без объяснений и попытки связаться.
                                    0
                                    Вы совсем читать не умеете? Вам про Фому, а вы — про Ерёму.
                                    Если у проекта есть парсинг — он делается не просто так и делаться от ваших действий он не перестанет, просто примет другие формы.
                                    0
                                    или можно было бы поднять нормальный API на отдельном серваке.
                                      +1
                                      даже за деньги многие не готовы к этому — владельцы сайтов я имею в виду
                                      0
                                      Всё очень правильно написано. Я занимаюсь парсингом более трёх лет. И за 3 года я видел только один сайт, который нельзя было спарсить. Точнее, в заданном промежутке времени я не нашёл решения. Для каждого сайта можно сделать парсер. И он будет эммулировать всё то, что Вы описали в статье и даже больше.
                                      Наиболее рациональная защита — это ограничение по количеству запросов с 1 ip в единицу времени. Большинство живых людей не будут сидеть и листать Ваш сайт час страницу за страницей. Либо же отдача какой-то части материала. Например, если человек ищет какую-то услугу в каком-то регионе, то ему отдаются не все 40000 результатов поиска, а 3000 первых записей. Для живого человека этого более, чем достаточно (именно так работает таже manta.com).
                                        +7
                                        Большинство живых людей не будут сидеть и листать Ваш сайт час страницу за страницей.
                                        Зато за одним proxy-IP может оказаться вдруг несколько тысяч (если не миллионов) живых людей.
                                      –5
                                      Сколько эти парсеры давали нагрузки, что "LA значительно рос на серверах". 100 запросов в секунду? 1000? Может проще было оптимизировать код, пересмотреть какие-то подходы, чтобы просто не замечать эту избыточную нагрузку? Ну а если с одного адреса проходит запредельное кол-во запросов на бэкенд мимо кешей, то можно поместить этот адрес в некий список, которому показываю только капчу. (У вас что-то подобное уже реализовано)
                                      Удивительно, я конечно не знаю всех деталей, но столько вложить сил в латание… по факту костылей.
                                        0
                                        В кои-то веки нормальная техническая статья про конфигурирование, а все комментарии про надо / не надо и не одного про конфиги. На хабре админы остались, вообще?
                                          +3
                                          Чуть выше автор признаёт, что это специфический костыль, обусловленный требованиями заказчика. Ну и не совсем понятно, какие комментарии про конфиги вы хотите услышать.
                                            +10
                                            В статье описан жуткий костыль и она скорее является примером как делать не надо, потому и реакция соответствующая.
                                            +2
                                            Статья и тематика спорная, но прочитал с интересом. Не хватает цифр — доля паразитного трафика до и после, какие меры из реализованных больше всего повлияли на итоговую фильтрацию.
                                              0
                                              PTR-записи у многих хостеров ставятся автоматом любые и никто не мешает им поставить ptr-записи google или яндекс и т.п. Поисковики рекомендуют потом еще обратное преобразование проверять.

                                              Еще может иметь смысл проверять IP-адреса по whois, тогда если whois правильный и соответствует яндексу/гуглу и т.п. — можно в белый список сразу всю их сеть внести и данные в whois подделать сложнее, чем PTR.
                                                +2
                                                geo $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 запросов за сутки. Разумеется никакими Яндекс-ботами здесь и не пахнет
                                                  +2
                                                  А что мешало реализовать динамическую роботоловилку с прослойкой перед внутри nginx на lua? Так делает, например, Wallarm.
                                                  По временным затратам вышло бы примерно тоже самое, но скорость и качество работы ни в сравнение.

                                                  Не критики для, а вопроса ради. Решение, как я понимаю, уже протестировано и работает в рамках ТЗ заказчика. Интересует, почему вы пришли именно к такому решению.

                                                  P.S. Соглашусь с комментарием jehy, что можно было не банить, а попытаться связаться с владельцами ботов. Отдавая хотя бы стандартную страницу "call me maybe"
                                                    0
                                                    Мешало отсутствие знаний lua и времени на его изучение. А так да, такая идея была.
                                                      +1
                                                      Стоило хотя бы попытаться. В него на удивление просто вникнуть.
                                                    +1
                                                    А мы боремся с теми, кто борется с нашими парсерами… Пока получается. Но мы не такие плохие (даже хорошие я бы сказал) — мы стараемся раскидывать нагрузку (запросы к сайту) в течение дня, а не разово пытаться скачать "весь Интернет".
                                                      0
                                                      Я иногда пишу парсеры как хобби. Даже у меня есть небольшие наработки по проходу сайтов и ваша защита так себе )
                                                      Лучше сделайте API, если вам кому-то нужны ваши продукты то пускай на них смотрят.

                                                      Если большой пик с одних адресов, то скорее всего идет «синхронизация», т.е. если бот не работал нужно добрать. А так инкремент как правило идет довольно быстро.

                                                      Когда парсерщим это надоесть они купят симку и модем и будут ходить на мобильную версию сайта с сети МТС и тд.
                                                      Мобильная версия накладывает ограничения на всякие капчи, а если забаните ip большой тройки сами плакать будете )
                                                        +2
                                                        Мне кажется странным, что интернет-магазин решил ограничить распространение данных из своего раздела Товаров (читай каталогов и прайс-листов). Обычно поступают наоборот. Может лучше было организовать API, а при детектировании бота выдавать ему подсказку с адресом этого API, чтобы облегчить жизнь админу этого бота?
                                                          +5
                                                          Я, чесно, не пойму, какую такую ужасную нагрузку несет nginx динамике c, хотя бы, 30 минутным кешем каталога товаров.
                                                          Ну ей богу, не пойму. При правильной настройке ключей (с cookies и без cookies) у вас отдельные кеши для всех и для зарегистрированных клиентов/редакторов.

                                                          И в таком случае, как правильно заметили выше, роляет рейт запросов на IP. Если динамика тупая и кривая и работает на калькуяторе.\

                                                          Подходит только под: не хочу делиться, хочу быть уникальным! Глупости это все и трата времени. Кто хочет — спарсит. Что не может — попросит того, что может. Я сам, когда парсил нужные мне ресурсы представлялся или хромом, или гуглом, всегда отправлял реферера и еще пачку заголовков, иногда и кукисы поддерживал, и все в 5-6 потоков. Иногда часть через прокси. Мде.
                                                            +2
                                                            Потому что современные интернет магазины не кешируют ничего даже для анонимов/гостей. Необходим анализ переходов пользователя для отображения спец предложений, похожих или возможно заинтересующих товаров. Маркетологи на крупных проектах сейчас пытаются каждые вероятные 0,5 процента выжать.

                                                            Подходит только под: не хочу делиться, хочу быть уникальным! Глупости это все и трата времени. Кто хочет — спарсит. Что не может — попросит того, что может. Я сам, когда парсил нужные мне ресурсы представлялся или хромом, или гуглом, всегда отправлял реферера и еще пачку заголовков, иногда и кукисы поддерживал, и все в 5-6 потоков. Иногда часть через прокси. Мде.
                                                            Вас можно вычислить. На раз — два. Приходилось использовать вебдрайвер (selenium, PhantomJS), правильно водить мышкой, изучать поведенческие шаблоны и пр. Покупать распознавание капчи наконец. А с учетом стоимости таких услуг иногда только 80$ в день улетало на распознавание капчи.
                                                              +1
                                                              >> Потому что современные интернет магазины не кешируют ничего даже для анонимов/гостей. Необходим анализ переходов пользователя для отображения спец предложений, похожих или возможно заинтересующих товаров. Маркетологи на крупных проектах сейчас пытаются каждые вероятные 0,5 процента выжать.

                                                              Это все можно на джаваскрипте асинхронно сделать.
                                                                +2
                                                                Не на раз-два. Стоимость обнаружения качественного парсера сравнима со стоимостью разработки такого парсера. Короче, тут типичная борьба оружия и брони.

                                                                Только куча комментов в топике намекает, что это глупая борьба, особенно — в данном случае.
                                                                  0
                                                                  Потому что современные интернет магазины не кешируют ничего даже для анонимов/гостей

                                                                  Вы их на мух и котлет разделяете при помощи cookies? 10 секундный кеш по ключу с кукисами решит вашу проблему. Просто добавить $cookiename к ключу.
                                                                  Даже для тех, кто не слушает советов, 1 секундный кеш и рейты в каждом случаю 100% решают задачу и коллосально снижают нагрузки. А всю обработку нужно повесить на javascript, как писали ниже. Ну или покупать железо мощнее.

                                                                  Вас можно вычислить. На раз — два.

                                                                  У вас есть такой сайт (или вы с ним сталкивались), который вычисляет таких парсеров на раз-два? Дайте ссылку в личку, я хочу посмотреть.
                                                                –1
                                                                Ой, ладно, какая нагрузка? Хотите с ней бороться — помогите парсерам, сделайте api. Или кешируйте данные. Или… Средств борьбы с нагрузкой куча!
                                                                Мне кажется, что вы просто боитесь за данные.
                                                                  +1
                                                                  На что только не пойдут люди, лиж бы не сделать файлик с каталогом, в котором все будет лежать в удобоваримой форме.
                                                                  Это какая-то борьба с ветряными мельницами, тем более что боты просто парсили контент.
                                                                  Впрочем, типично российский подход к решению проблем, все запретить, никого не пускать, пускай клиенты монитор разобьют из-за капчей.
                                                                    0
                                                                    Ну да, вместо того чтобы сделать удобный АПИ, давайте объявим потребителей злоумышленниками! Совковый маразм!
                                                                      +2
                                                                      Сказ о потраченных человекочасах.
                                                                        +4
                                                                        Как владелец СП-сайта (и тот, кто писал парсеры сайтов поставщиков для своих организаторов закупок) хотелось бы заметить, что если бы компании, подобные вашим клиентам, не были бы упоротыми и предоставляли бы прайс-листы в машиночитаемом виде и со ссылками на картинки (для выкачивания), а не кучей разовых соплей и пони какающих радугой, то количество подобныъ парсеров устремилось бы к нулю.
                                                                          0
                                                                          С одной стороны, понимаю Вас, и в чем-то согласен, так было бы всем проще жить. С другой, понимаю и таких клиентов: они тратят ресурс на наполнение контентом своих проектов, а это потом все пытаются бесплатно утащить.
                                                                            +1
                                                                            Так о том и речь: если бы они «нормально» вели себя с контрагентами и затруднились бы задуматься о специфике работы (более того, машиночитаемые прайслисты требуют намного меньше работы для наполнения, чем эта радужная каша, которую они присылают), то мало кому нужно было бы бесплатно таскать у них.

                                                                            Тут, конечно, остаётся открытым вопрос о конкурентах, но это решается вотермарком: СП он не мешает (люди итак знают у какого поставщика будет покупаться продукция, так что коллизия вотермарков исключена), а конкурентам насолит.

                                                                        Only users with full accounts can post comments. Log in, please.