Как обнаружить поддельных пользователей (сканеров) и cURL

Некоторые другие сайты используют cURL и поддельный http-референт для копирования содержимого моего сайта. У нас есть какой-либо способ обнаружить cURL или нет реального веб-браузера?

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

Я был в беде слишком много лет назад, и мой первый совет: если у вас есть время, будьте роботом (я предполагаю, что «гусеница» – это парень, который сканирует ваш сайт), это лучшая школа для этого предмета. Просканировав несколько веб-сайтов, я узнал различные способы защиты, и, связывая их, я был эффективным.

Я даю вам несколько примеров мер защиты, которые вы можете попробовать.


Сессии на IP-адрес

Если пользователь использует 50 новых сеансов каждую минуту, вы можете подумать, что этот пользователь может быть искателем, который не обрабатывает файлы cookie. Конечно, завиток отлично управляет файлами cookie, но если вы соединяете его с счетчиком посещений за сеанс (объясняется позже) или если ваш искатель является noobie с вопросами cookie, он может быть эффективным.

Трудно представить, что 50 человек одной и той же общей связи получат одновременный доступ на ваш сайт (это, конечно, зависит от вашего трафика, это зависит от вас). И если это произойдет, вы можете заблокировать страницы своего веб-сайта, пока не будет заполнена надпись.

Идея:

1) вы создаете 2 таблицы: 1 для сохранения запрещенных ips и 1 для сохранения ip и сеансов

 create table if not exists sessions_per_ip ( ip int unsigned, session_id varchar(32), creation timestamp default current_timestamp, primary key(ip, session_id) ); create table if not exists banned_ips ( ip int unsigned, creation timestamp default current_timestamp, primary key(ip) ); 

2) в начале вашего скрипта вы удаляете слишком старые записи из обеих таблиц

3) Затем вы проверяете, запрещен ли ip вашего пользователя или нет (вы устанавливаете флаг в true)

4) Если нет, вы считаете, сколько у него сеансов для его ip

5) если у него слишком много сеансов, вы вставляете его в свой запрещенный стол и устанавливаете флаг

6) вы вставляете свой ip в сеансы на ip-таблицу, если он еще не вставлен

Я написал образец кода, чтобы лучше показать мою идею.

 <?php try { // Some configuration (small values for demo) $max_sessions = 5; // 5 sessions/ip simultaneousely allowed $check_duration = 30; // 30 secs max lifetime of an ip on the sessions_per_ip table $lock_duration = 60; // time to lock your website for this ip if max_sessions is reached // Mysql connection require_once("config.php"); $dbh = new PDO("mysql:host={$host};dbname={$base}", $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Delete old entries in tables $query = "delete from sessions_per_ip where timestampdiff(second, creation, now()) > {$check_duration}"; $dbh->exec($query); $query = "delete from banned_ips where timestampdiff(second, creation, now()) > {$lock_duration}"; $dbh->exec($query); // Get useful info attached to our user... session_start(); $ip = ip2long($_SERVER['REMOTE_ADDR']); $session_id = session_id(); // Check if IP is already banned $banned = false; $count = $dbh->query("select count(*) from banned_ips where ip = '{$ip}'")->fetchColumn(); if ($count > 0) { $banned = true; } else { // Count entries in our db for this ip $query = "select count(*) from sessions_per_ip where ip = '{$ip}'"; $count = $dbh->query($query)->fetchColumn(); if ($count >= $max_sessions) { // Lock website for this ip $query = "insert ignore into banned_ips ( ip ) values ( '{$ip}' )"; $dbh->exec($query); $banned = true; } // Insert a new entry on our db if user's session is not already recorded $query = "insert ignore into sessions_per_ip ( ip, session_id ) values ('{$ip}', '{$session_id}')"; $dbh->exec($query); } // At this point you have a $banned if your user is banned or not. // The following code will allow us to test it... // We do not display anything now because we'll play with sessions : // to make the demo more readable I prefer going step by step like // this. ob_start(); // Displays your current sessions echo "Your current sessions keys are : <br/>"; $query = "select session_id from sessions_per_ip where ip = '{$ip}'"; foreach ($dbh->query($query) as $row) { echo "{$row['session_id']}<br/>"; } // Display and handle a way to create new sessions echo str_repeat('<br/>', 2); echo '<a href="' . basename(__FILE__) . '?new=1">Create a new session / reload</a>'; if (isset($_GET['new'])) { session_regenerate_id(); session_destroy(); header("Location: " . basename(__FILE__)); die(); } // Display if you're banned or not echo str_repeat('<br/>', 2); if ($banned) { echo '<span style="color:red;">You are banned: wait 60secs to be unbanned... a captcha must be more friendly of course!</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/feu-rouge.png" />'; } else { echo '<span style="color:blue;">You are not banned!</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/traffic_light_green.png" />'; } ob_end_flush(); } catch (PDOException $e) { /*echo*/ $e->getMessage(); } ?> 

Посетить Counter

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

Идея:

  1. Создайте массив в сеансе пользователя, он будет содержать время посещения () s.
  2. Удаление посещений старше X секунд в этом массиве
  3. Добавить новую запись для фактического посещения
  4. Количество записей в этом массиве
  5. Запретить пользователю, если он посетил страницы Y

Образец кода :

 <?php $visit_counter_pages = 5; // maximum number of pages to load $visit_counter_secs = 10; // maximum amount of time before cleaning visits session_start(); // initialize an array for our visit counter if (array_key_exists('visit_counter', $_SESSION) == false) { $_SESSION['visit_counter'] = array(); } // clean old visits foreach ($_SESSION['visit_counter'] as $key => $time) { if ((time() - $time) > $visit_counter_secs) { unset($_SESSION['visit_counter'][$key]); } } // we add the current visit into our array $_SESSION['visit_counter'][] = time(); // check if user has reached limit of visited pages $banned = false; if (count($_SESSION['visit_counter']) > $visit_counter_pages) { // puts ip of our user on the same "banned table" as earlier... $banned = true; } // At this point you have a $banned if your user is banned or not. // The following code will allow us to test it... echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>'; // Display counter $count = count($_SESSION['visit_counter']); echo "You visited {$count} pages."; echo str_repeat('<br/>', 2); echo <<< EOT <a id="reload" href="#">Reload</a> <script type="text/javascript"> $('#reload').click(function(e) { e.preventDefault(); window.location.reload(); }); </script> EOT; echo str_repeat('<br/>', 2); // Display if you're banned or not echo str_repeat('<br/>', 2); if ($banned) { echo '<span style="color:red;">You are banned! Wait for a short while (10 secs in this demo)...</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/feu-rouge.png" />'; } else { echo '<span style="color:blue;">You are not banned!</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/traffic_light_green.png" />'; } ?> с <?php $visit_counter_pages = 5; // maximum number of pages to load $visit_counter_secs = 10; // maximum amount of time before cleaning visits session_start(); // initialize an array for our visit counter if (array_key_exists('visit_counter', $_SESSION) == false) { $_SESSION['visit_counter'] = array(); } // clean old visits foreach ($_SESSION['visit_counter'] as $key => $time) { if ((time() - $time) > $visit_counter_secs) { unset($_SESSION['visit_counter'][$key]); } } // we add the current visit into our array $_SESSION['visit_counter'][] = time(); // check if user has reached limit of visited pages $banned = false; if (count($_SESSION['visit_counter']) > $visit_counter_pages) { // puts ip of our user on the same "banned table" as earlier... $banned = true; } // At this point you have a $banned if your user is banned or not. // The following code will allow us to test it... echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>'; // Display counter $count = count($_SESSION['visit_counter']); echo "You visited {$count} pages."; echo str_repeat('<br/>', 2); echo <<< EOT <a id="reload" href="#">Reload</a> <script type="text/javascript"> $('#reload').click(function(e) { e.preventDefault(); window.location.reload(); }); </script> EOT; echo str_repeat('<br/>', 2); // Display if you're banned or not echo str_repeat('<br/>', 2); if ($banned) { echo '<span style="color:red;">You are banned! Wait for a short while (10 secs in this demo)...</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/feu-rouge.png" />'; } else { echo '<span style="color:blue;">You are not banned!</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/traffic_light_green.png" />'; } ?> 

Изображение для загрузки

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

Эта идея (я думаю, самая элегантная и простая в использовании) использует mod_rewrite, чтобы скрыть код в файле .jpg / .png / …. Это изображение должно быть доступно на каждой странице, которую вы хотите защитить: это может быть ваш логотип, но вы выберете изображение небольшого размера (потому что это изображение нельзя кэшировать).

Идея:

1 / Добавьте эти строки в ваш .htaccess

 RewriteEngine On RewriteBase /tests/anticrawl/ RewriteRule ^logo\.jpg$ logo.php 

2 / Создайте свой логотип.php с защитой

 <?php // start session and reset counter session_start(); $_SESSION['no_logo_count'] = 0; // forces image to reload next time header("Cache-Control: no-store, no-cache, must-revalidate"); // displays image header("Content-type: image/jpg"); readfile("logo.jpg"); die(); 

3 / Увеличьте свой no_logo_count на каждой странице, чтобы добавить безопасность, и проверьте, достиг ли он вашего предела.

Образец кода :

 <?php $no_logo_limit = 5; // number of allowd pages without logo // start session and initialize session_start(); if (array_key_exists('no_logo_count', $_SESSION) == false) { $_SESSION['no_logo_count'] = 0; } else { $_SESSION['no_logo_count']++; } // check if user has reached limit of "undownloaded image" $banned = false; if ($_SESSION['no_logo_count'] >= $no_logo_limit) { // puts ip of our user on the same "banned table" as earlier... $banned = true; } // At this point you have a $banned if your user is banned or not. // The following code will allow us to test it... echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>'; // Display counter echo "You did not loaded image {$_SESSION['no_logo_count']} times."; echo str_repeat('<br/>', 2); // Display "reload" link echo <<< EOT <a id="reload" href="#">Reload</a> <script type="text/javascript"> $('#reload').click(function(e) { e.preventDefault(); window.location.reload(); }); </script> EOT; echo str_repeat('<br/>', 2); // Display "show image" link : note that we're using .jpg file echo <<< EOT <div id="image_container"> <a id="image_load" href="#">Load image</a> </div> <br/> <script type="text/javascript"> // On your implementation, you'llO of course use <img src="logo.jpg" /> $('#image_load').click(function(e) { e.preventDefault(); $('#image_load').html('<img src="logo.jpg" />'); }); </script> EOT; // Display if you're banned or not echo str_repeat('<br/>', 2); if ($banned) { echo '<span style="color:red;">You are banned: click on "load image" and reload...</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/feu-rouge.png" />'; } else { echo '<span style="color:blue;">You are not banned!</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/traffic_light_green.png" />'; } ?> 

Проверка файлов cookie

Вы можете создавать файлы cookie на стороне javascript, чтобы проверить, не интерпретируют ли ваши пользователи javascript (например, искатель, использующий Curl).

Идея довольно проста: это примерно то же, что и проверка изображения.

  1. Установите значение $ _SESSION в 1 и увеличивайте его в каждом посещении
  2. если cookie (установленный в JavaScript) существует, установите значение сеанса равным 0
  3. если это значение достигло предела, запретите пользователю

Код:

 <?php $no_cookie_limit = 5; // number of allowd pages without cookie set check // Start session and reset counter session_start(); if (array_key_exists('cookie_check_count', $_SESSION) == false) { $_SESSION['cookie_check_count'] = 0; } // Initializes cookie (note: rename it to a more discrete name of course) or check cookie value if ((array_key_exists('cookie_check', $_COOKIE) == false) || ($_COOKIE['cookie_check'] != 42)) { // Cookie does not exist or is incorrect... $_SESSION['cookie_check_count']++; } else { // Cookie is properly set so we reset counter $_SESSION['cookie_check_count'] = 0; } // Check if user has reached limit of "cookie check" $banned = false; if ($_SESSION['cookie_check_count'] >= $no_cookie_limit) { // puts ip of our user on the same "banned table" as earlier... $banned = true; } // At this point you have a $banned if your user is banned or not. // The following code will allow us to test it... echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>'; // Display counter echo "Cookie check failed {$_SESSION['cookie_check_count']} times."; echo str_repeat('<br/>', 2); // Display "reload" link echo <<< EOT <br/> <a id="reload" href="#">Reload</a> <br/> <script type="text/javascript"> $('#reload').click(function(e) { e.preventDefault(); window.location.reload(); }); </script> EOT; // Display "set cookie" link echo <<< EOT <br/> <a id="cookie_link" href="#">Set cookie</a> <br/> <script type="text/javascript"> // On your implementation, you'll of course put the cookie set on a $(document).ready() $('#cookie_link').click(function(e) { e.preventDefault(); var expires = new Date(); expires.setTime(new Date().getTime() + 3600000); document.cookie="cookie_check=42;expires=" + expires.toGMTString(); }); </script> EOT; // Display "unset cookie" link echo <<< EOT <br/> <a id="unset_cookie" href="#">Unset cookie</a> <br/> <script type="text/javascript"> // On your implementation, you'll of course put the cookie set on a $(document).ready() $('#unset_cookie').click(function(e) { e.preventDefault(); document.cookie="cookie_check=;expires=Thu, 01 Jan 1970 00:00:01 GMT"; }); </script> EOT; // Display if you're banned or not echo str_repeat('<br/>', 2); if ($banned) { echo '<span style="color:red;">You are banned: click on "Set cookie" and reload...</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/feu-rouge.png" />'; } else { echo '<span style="color:blue;">You are not banned!</span>'; echo '<br/>'; echo '<img src="http://img.ruphp.com/spam-prevention/traffic_light_green.png" />'; } 

Защита от прокси

Несколько слов о разных прокси-серверах, которые мы можем найти в Интернете:

  • «Обычный» прокси отображает информацию о подключении пользователя (в частности, его IP)
  • Анонимный прокси не отображает IP, но дает информацию об использовании прокси-сервера в заголовке.
  • Высоко-анонимный прокси не отображает пользовательский IP-адрес и не отображает информацию, которую браузер не может отправить.

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

Некоторые переменные $ _SERVER могут содержать ключи, особенно если ваши пользователи находятся за прокси (исчерпывающий список из этого вопроса ):

  • CLIENT_IP
  • FORWARDED
  • FORWARDED_FOR
  • FORWARDED_FOR_IP
  • HTTP_CLIENT_IP
  • HTTP_FORWARDED
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED_FOR_IP
  • HTTP_PC_REMOTE_ADDR
  • HTTP_PROXY_CONNECTION»
  • HTTP_VIA
  • HTTP_X_FORWARDED
  • HTTP_X_FORWARDED_FOR
  • HTTP_X_FORWARDED_FOR_IP
  • HTTP_X_IMFORWARDS
  • HTTP_XROXY_CONNECTION
  • С ПОМОЩЬЮ
  • X_FORWARDED
  • X_FORWARDED_FOR

Вы можете указать другое поведение (нижние пределы и т. Д.) На свои антиспаллинговые ценные бумаги, если вы обнаружите один из этих ключей в переменной $_SERVER .


Вывод

Существует множество способов обнаружения злоупотреблений на вашем сайте, поэтому вы найдете решение наверняка. Но вам нужно точно знать, как используется ваш сайт, поэтому ваши ценные бумаги не будут агрессивны с вашими «нормальными» пользователями.

Помните: HTTP не волшебный. Существует определенный набор заголовков, отправленных с каждым HTTP-запросом; если эти заголовки отправляются веб-браузером, их также можно отправить любой программой – включая cURL (и libcurl).

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

ОБНОВЛЕНИЕ: Как правильно заметил unr3al011, curl не выполняет JavaScript, поэтому теоретически возможно создать страницу, которая будет вести себя по-разному, если рассматривать grabbers (например, с настройкой и, позже, проверкой определенного файла cookie на JS).

Тем не менее, это была бы очень хрупкая оборона. Данные страницы все равно должны были быть захвачены с сервера – и этот HTTP-запрос (и это всегда HTTP-запрос) можно эмулировать с помощью curl. Проверьте этот ответ, например, как победить такую ​​защиту.

… и я даже не упомянул, что некоторые грабители могут выполнять JavaScript. )

Способ избежать поддельных ссылок – отслеживание пользователя

Вы можете отслеживать пользователя одним или несколькими из следующих способов:

  1. Сохраните файл cookie в клиенте браузера с некоторым специальным кодом (например: последний URL-адрес, отметка времени) и проверьте его в каждом ответе вашего сервера.

  2. То же, что и раньше, но используя сеансы вместо явных файлов cookie

Для файлов cookie вы должны добавить криптографическую безопасность.

 [Cookie] url => http://someurl/ hash => dsafdshfdslajfd 

Таким образом, хеш вычисляется на PHP

 $url = $_COOKIE['url']; $hash = $_COOKIE['hash']; $secret = 'This is a fixed secret in the code of your application'; $isValidCookie = (hash('algo', $secret . $url) === $hash); $isValidReferer = $isValidCookie & ($_SERVER['HTTP_REFERER'] === $url) 

Поскольку некоторые из них упомянули, что cURL не может выполнить JavaScritp (насколько мне известно), поэтому вы можете попытаться настроить someting, например raina77ow, но это не будет wokrk для других grabbers / donwloaders.

Я предлагаю вам попробовать создать ловушку для ботов таким образом, чтобы вы справлялись с захватчиками / загрузчиками, которые могут выполнять JavaScript.

Я не знаю ни одного решения, чтобы полностью предотвратить это, поэтому моя лучшая рекомендация – попробовать несколько решений:

1) позволяют только известные пользовательские агенты, такие как все основные браузеры в вашем файле .htaccess

2) Настройте свой robots.txt для предотвращения ботов

3) Настройте ловушку бота для ботов, которые не уважают файл robots.txt

поместите это в корневую папку как файл .htaccess . это может помочь. Я нашел его на одном веб-сайте провайдера хостинга, но не знаю, что это значит 🙂

 SetEnvIf User-Agent ^Teleport graber SetEnvIf User-Agent ^w3m graber SetEnvIf User-Agent ^Offline graber SetEnvIf User-Agent Downloader graber SetEnvIf User-Agent snake graber SetEnvIf User-Agent Xenu graber Deny from env=graber