Jak wykrywać fałszywych użytkowników (crawlery) i zwijać

Niektóre inne strony używają cURL i fałszywego referera http, aby skopiować zawartość mojej witryny. Czy mamy jakiś sposób, aby wykryć cURL lub nie prawdziwa przeglądarka internetowa ?

Author: Alain Tiemblo, 2012-09-04

6 answers

Nie ma magicznego rozwiązania, aby uniknąć automatycznego raczkowania. Wszystko, co człowiek może zrobić, robot też może to zrobić. Istnieją tylko rozwiązania, które utrudniają pracę, tak trudne, że tylko silni wykwalifikowani maniacy mogą próbować je przekazać.

Ja też miałem kłopoty kilka lat temu i moją pierwszą radą jest to, że jeśli masz czas, sam bądź crawlerem (zakładam, że "crawler" to Facet, który indeksuje Twoją stronę), jest to najlepsza szkoła na ten temat. Indeksując kilka stron internetowych, nauczyłem się różnego rodzaju ochrony, a poprzez ich powiązanie byłem skuteczny.

Podaję kilka przykładów zabezpieczeń, które możesz wypróbować.


Sesje na IP

Jeśli użytkownik korzysta z 50 nowych sesji co minutę, możesz myśleć, że ten użytkownik może być crawlerem, który nie obsługuje plików cookie. Oczywiście curl doskonale zarządza plikami cookie, ale jeśli połączysz je z licznikiem odwiedzin na sesję (wyjaśnione później) lub jeśli twój robot jest noobie z kwestiami cookie, może to być skuteczne.

Jest trudno sobie wyobrazić, że 50 osób z tego samego wspólnego połączenia dostanie się jednocześnie na swojej stronie internetowej(to oczywiście zależy od ruchu, to zależy od Ciebie). Jeśli tak się stanie, możesz zablokować strony swojej witryny do momentu wypełnienia captcha.

Idea:

1) tworzysz 2 tabele : 1 do zapisywania zbanowanych adresów IP i 1 do zapisywania ip i sesji

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) na początku skryptu usuwasz zbyt stare wpisy z obu tabel

3) Następnie sprawdzasz czy ip użytkownika is banned or not (ustawiasz flagę na true)

4) Jeśli nie, policz ile ma sesji dla swojego ip

5) jeśli ma za dużo sesji, wstawiasz ją do swojej zbanowanej tabeli i ustawiasz flagę

6) wstawiasz jego ip do tabeli sesji na ip, jeśli nie został on już wstawiony

Napisałem próbkę kodu, aby lepiej pokazać mój pomysł.

<?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();
}

?>

Licznik Odwiedzin

Jeśli użytkownik używa tego samego pliku cookie do indeksowania stron, będziesz mógł użyć jego sesja, aby go zablokować. Ten pomysł jest dość prosty: czy to możliwe, że użytkownik odwiedza 60 stron w 60 sekund?

Idea:

    W tym celu należy utworzyć tablicę, która będzie zawierała czas odwiedzin()s.
  1. Usuń wizyty starsze niż X sekund w tej tablicy
  2. Dodaj nowy wpis do aktualnej wizyty
  3. licz wpisy w tej tablicy
  4. Ban użytkownika, jeśli odwiedzał strony Y

Przykładowy kod:

<?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" />';
}
?>

Obrazek do Pobierz

Kiedy robot musi wykonać swoją brudną robotę, to za dużą ilość danych i w jak najkrótszym czasie. Dlatego nie pobierają obrazów na stronach ; zajmuje to zbyt dużo pasma i sprawia, że indeksowanie jest wolniejsze.

Ten pomysł (myślę, że najbardziej elegancki i najłatwiejszy do wdrożenia) wykorzystuje mod_rewrite do ukrycia kodu w.jpgpng / ... plik z obrazkiem. Ten obraz powinien być dostępny na każdej stronie, którą chcesz chronić : może to być Twoja strona z logo, ale zostanie wybrany obraz o małych rozmiarach (ponieważ obraz ten nie może być buforowany).

Idea:

1 / Dodaj te wiersze do swoich .htaccess

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

2/ stwórz swoje logo.php z zabezpieczeniem

<?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 / Zwiększ swoje no_logo_count na każdej stronie musisz dodać zabezpieczenia i sprawdzić, czy osiągnął Twój limit.

Przykładowy kod:

<?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" />';
}
?>

Sprawdzanie ciasteczek

Możesz tworzyć pliki cookie po stronie javascript, aby sprawdzić, czy użytkownicy interpretują javascript (na przykład crawler korzystający z Curl nie).

Pomysł jest dość prosty : jest to mniej więcej to samo, co kontrola obrazu.

  1. ustaw wartość $_SESSION na 1 i zwiększ ją przy każdej wizycie
  2. Jeśli plik cookie (ustawiony w JavaScript) istnieje, ustaw wartość sesji na 0
  3. jeśli ta wartość osiągnęła limit, Zablokuj użytkownika

Kod:

<?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" />';
}

Ochrona przed proxy

Kilka słów o różnych rodzajach proxy możemy znaleźć na www:

    Serwer proxy wyświetla informacje o połączeniu użytkownika (w szczególności jego IP).]}
  • anonimowy serwer proxy nie wyświetla adresu IP, ale podaje informacje o użyciu serwera proxy w nagłówku.
  • wysoce anonimowy serwer proxy nie wyświetla IP użytkownika i nie wyświetla żadnych informacji, których przeglądarka może nie wysłać.

Łatwo jest znaleźć proxy do podłączenia dowolnej strony internetowej, ale bardzo trudno jest znaleźć wysokiej anonimowe proxy.

Niektóre zmienne $_SERVER mogą zawierać klucze szczególnie, jeśli użytkownicy są za proxy (wyczerpująca lista pochodzi z to pytanie):

  • CLIENT_IP
  • forward
  • 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
  • VIA
  • X_FORWARDED
  • X_FORWARDED_FOR

Ty może nadać inne zachowanie (niższe limity itp.) Twoim zabezpieczeniom anty crawl, jeśli wykryjesz jeden z tych kluczy w zmiennej $_SERVER.


Podsumowanie

Istnieje wiele sposobów wykrywania nadużyć na swojej stronie internetowej, więc na pewno znajdziesz rozwiązanie. Ale musisz dokładnie wiedzieć, w jaki sposób Twoja strona jest używana, aby Twoje papiery wartościowe nie były agresywne dla "normalnych" użytkowników.

 94
Author: Alain Tiemblo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:58

Pamiętaj: HTTP to nie magia. Istnieje zdefiniowany zestaw nagłówków wysyłanych z każdym żądaniem HTTP; jeśli te nagłówki są wysyłane przez przeglądarkę internetową, mogą być również wysyłane przez dowolny program-w tym cURL (i libcurl).

Niektórzy uważają to za przekleństwo, ale z drugiej strony jest to błogosławieństwo, ponieważ znacznie upraszcza testowanie funkcjonalne aplikacji internetowych.

UPDATE: jak słusznie zauważył unr3al011, curl nie wykonuje JavaScript, więc teoretycznie możliwe jest utworzenie strony, która będzie zachowywał się inaczej, gdy grabbers ogląda (na przykład, z ustawieniem i, Później, sprawdzanie określonego pliku cookie za pomocą js).

Mimo to, to byłaby bardzo delikatna obrona. Dane strony nadal musiały zostać pobrane z serwera - a to żądanie HTTP (i to zawsze HTTP request) może być emulowane przez curl. Sprawdź ta odpowiedź na przykład, jak pokonać taką obronę.

... i nawet nie wspomniałem, że niektórzy grabberowie w stanie wykonać JavaScript. )

 2
Author: raina77ow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:19

Sposobem uniknięcia fałszywych refererów jest śledzenie użytkownika

Możesz śledzić użytkownika za pomocą jednej lub więcej z tych metod:

  1. Zapisz plik cookie w kliencie przeglądarki za pomocą specjalnego kodu (np. ostatnio odwiedzany adres url, znacznik czasu) i zweryfikuj go w każdej odpowiedzi serwera.

  2. To samo co wcześniej, ale używanie sesji zamiast jawnych plików cookie

Dla plików cookie należy dodać zabezpieczenia kryptograficzne, takie jak.

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

Hash jest obliczany w PHP przez this way

$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)
 0
Author: Maks3w,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-13 07:47:46

Możesz wykryć cURL-Useragent za pomocą następującej metody. Ale ostrzegam, że useragent może zostać nadpisany przez użytkownika, w każdym razie domyślne ustawienia mogą być rozpoznawane przez:

function is_curl() {
    if (stristr($_SERVER["HTTP_USER_AGENT"], 'curl'))
        return true;
}
 0
Author: Fusca Software,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-10 23:58:41

Jak już niektórzy wspominali cURL nie może wykonać Javascriptp (z mojej wiedzy) więc można ewentualnie spróbować ustawić coś takiego jak raina77ow sugerują, ale to nie byłoby wokrk dla innych grabbers/donwloaders.

Proponuję spróbować zbudować Bot trap w ten sposób radzisz sobie z grabberami/downloaderami, którzy mogą wykonywać JavaScript.

Nie znam żadnego 1 rozwiązania, aby w pełni temu zapobiec, więc moją najlepszą rekomendacją byłoby wypróbowanie wielu rozwiązań:

1) Zezwalaj tylko na znane agentów użytkowników, takich jak wszystkie główne przeglądarki w Twoim .plik htaccess

2) Ustaw swoje roboty.txt aby zapobiec botom

3) Ustaw pułapkę botów na boty, które nie szanują robotów.plik txt

 -1
Author: Rayvyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-12 18:20:58

Umieść to w folderze głównym jako plik {[1] }. to może pomóc. Znalazłem go na jednej stronie dostawcy webhostingu, ale nie wiem, co to znaczy:)

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
 -3
Author: Marcel Gent Simonis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-05 13:49:12