Как обнаружить поддельных пользователей (сканеры) и cURL
Некоторые другие веб-сайты используют cURL и поддельный http referer для копирования содержимого моего веб-сайта. У нас есть какой-нибудь способ обнаружить cURL или нет реального веб-браузера?
6 ответов
Не существует волшебного решения, позволяющего избежать автоматического сканирования. Все, что может сделать человек, может сделать и робот. Есть только решения, чтобы сделать работу более сложной, настолько трудной, что только сильные умники могут ее пройти.
У меня тоже были проблемы несколько лет назад, и мой первый совет: если у вас есть время, сами станьте гусеничным (я предполагаю, что "гусеничным" является тот, кто сканирует ваш сайт), это лучшая школа по этому предмету. Просматривая несколько веб-сайтов, я узнал о различных видах защиты и, связывая их, я был эффективным.
Я даю вам несколько примеров защиты, которые вы можете попробовать.
Сессий по IP
Если пользователь использует 50 новых сеансов каждую минуту, вы можете подумать, что этот пользователь может быть сканером, который не обрабатывает файлы cookie. Конечно, curl отлично управляет файлами cookie, но если вы объединяете их со счетчиком посещений за сеанс (объяснение позже), или если ваш сканер является новичком с вопросами 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://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
}
else
{
echo '<span style="color:blue;">You are not banned!</span>';
echo '<br/>';
echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/traffic_light_green.png" />';
}
ob_end_flush();
}
catch (PDOException $e)
{
/*echo*/ $e->getMessage();
}
?>
Счетчик посещений
Если ваш пользователь использует тот же файл cookie для сканирования ваших страниц, вы сможете использовать его сеанс для его блокировки. Эта идея довольно проста: возможно ли, что ваш пользователь посещает 60 страниц за 60 секунд?
Идея:
- Создайте массив в пользовательской сессии, он будет содержать время посещения (с).
- Удалить посещения старше X секунд в этом массиве
- Добавить новую запись для фактического посещения
- Количество записей в этом массиве
- Забаните своего пользователя, если он посетил 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://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
}
else
{
echo '<span style="color:blue;">You are not banned!</span>';
echo '<br/>';
echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/traffic_light_green.png" />';
}
?>
Изображение для загрузки
Когда сканер должен выполнить свою грязную работу, это делается для большого количества данных и в кратчайшие сроки. Вот почему они не загружают изображения на страницах; это занимает слишком много времени и замедляет ползание.
Эта идея (я думаю, что наиболее простой и простой в реализации) использует mod_rewrite, чтобы скрыть код в.jpg /.png /… файле изображения. Это изображение должно быть доступно на каждой странице, которую вы хотите защитить: это может быть ваш логотип, но вы выберете изображение небольшого размера (потому что это изображение не должно кэшироваться).
Идея:
1 / Добавьте эти строки в ваш.htaccess
RewriteEngine On
RewriteBase /tests/anticrawl/
RewriteRule ^logo\.jpg$ logo.php
2 / Создайте свой logo.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://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
}
else
{
echo '<span style="color:blue;">You are not banned!</span>';
echo '<br/>';
echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/traffic_light_green.png" />';
}
?>
Проверка файлов cookie
Вы можете создавать файлы cookie на стороне javascript, чтобы проверить, интерпретируют ли ваши пользователи javascript (например, сканер, использующий Curl).
Идея довольно проста: это примерно так же, как проверка изображения.
- Установите значение $_SESSION равным 1 и увеличивайте его при каждом посещении
- если cookie (установленный в JavaScript) существует, установите значение сеанса равным 0
- если это значение достигло предела, забаньте своего пользователя
Код:
<?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://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
}
else
{
echo '<span style="color:blue;">You are not banned!</span>';
echo '<br/>';
echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/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, поэтому теоретически можно создать страницу, которая будет вести себя по-разному при просмотре грабберами (например, с настройкой и, позже, проверкой определенного cookie с помощью JS).
Тем не менее, это была бы очень хрупкая защита. Данные страницы все еще должны были быть получены с сервера - и этот HTTP-запрос (и это всегда HTTP-запрос) можно эмулировать с помощью curl. Проверьте этот ответ на примере того, как победить такую защиту.
... и я даже не упомянул, что некоторые грабберы способны выполнять JavaScript.)
Вы можете обнаружить cURL-Useragent следующим способом. Но имейте в виду, что пользовательский агент может быть перезаписан пользователем, в любом случае настройки по умолчанию могут быть распознаны:
function is_curl() {
if (stristr($_SERVER["HTTP_USER_AGENT"], 'curl'))
return true;
}
Способ избежать фальшивых рефереров - это отслеживание пользователя
Вы можете отслеживать пользователя одним или несколькими из следующих способов:
Сохраните файл cookie в клиенте браузера с некоторым специальным кодом (например, последний посещенный URL, отметка времени) и проверяйте его в каждом ответе вашего сервера.
То же самое, что и раньше, но использование сеансов вместо явных файлов 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 (насколько мне известно), вы можете попытаться настроить что-то вроде предложения raina77ow, но это не сработало бы для других захватчиков / загрузчиков.
Я предлагаю вам попробовать построить ловушку для ботов таким образом, чтобы вы имели дело с грабберами / загрузчиками, которые могут выполнять 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