web-dev-qa-db-fra.com

Comment détecter les faux utilisateurs (crawlers) et cURL

Un autre site Web utilise cURL et un faux référenceur http pour copier le contenu de mon site Web . Avons-nous un moyen de détecter cURL ou un navigateur Web réel?

38
Ken Le

Il n'y a pas de solution magique pour éviter l'exploration automatique. Tout ce qu'un humain peut faire, un robot peut le faire aussi. Il n'y a que des solutions pour rendre le travail plus dur, si difficile que seuls des geeks compétents peuvent essayer de les réussir.

J'avais aussi des problèmes il y a quelques années et mon premier conseil est que, si vous en avez le temps, soyez un robot vous-même (je suppose qu'un "robot" est le type qui explore votre site Web), c'est la meilleure école pour le sujet. En explorant plusieurs sites Web, j’ai appris différents types de protection et en les associant, j’ai été efficace.

Je vous donne quelques exemples de protections que vous pouvez essayer.


Sessions par IP

Si un utilisateur utilise 50 nouvelles sessions chaque minute, vous pouvez penser que cet utilisateur pourrait être un robot d'exploration qui ne gère pas les cookies. Bien sûr, curl gère les cookies de manière parfaite, mais si vous le couplez avec un compteur de visites par session (expliqué plus loin), ou si votre robot d'exploration est un noobie avec des problèmes de cookies, il peut être efficace.

Il est difficile d’imaginer que 50 personnes de la même connexion partagée auront simultanément votre site Web (cela dépend bien sûr de votre trafic, cela dépend de vous). Et si cela se produit, vous pouvez verrouiller les pages de votre site Web jusqu'à ce qu'un captcha soit rempli.

Idée:

1) vous créez 2 tables: 1 pour enregistrer des ips interdits et 1 pour enregistrer des adresses IP et des sessions

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) au début de votre script, vous supprimez des entrées trop anciennes des deux tables

3) ensuite, vous vérifiez si l'adresse IP de votre utilisateur est bannie ou non (vous définissez un drapeau sur true)

4) si non, vous comptez combien il a des sessions pour son ip

5) s’il a trop de sessions, vous l’insérez dans votre table bannie et placez un drapeau

6) vous insérez son ip sur la table des sessions par ip s'il n'a pas déjà été inséré

J'ai écrit un exemple de code pour mieux illustrer mon idée.

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

?>

Compteur de visites

Si votre utilisateur utilise le même cookie pour explorer vos pages, vous pourrez utiliser sa session pour la bloquer. Cette idée est assez simple: est-il possible que votre utilisateur visite 60 pages en 60 secondes?

Idée:

  1. Créez un tableau dans la session utilisateur, il contiendra l'heure de la visite ().
  2. Supprimer les visites de plus de X secondes dans ce tableau
  3. Ajouter une nouvelle entrée pour la visite réelle 
  4. Compter les entrées dans ce tableau
  5. Interdit votre utilisateur s’il a visité Y pages

Exemple de code:

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

Une image à télécharger

Lorsqu'un robot d'exploration doit faire son sale boulot, c'est pour une grande quantité de données et dans les meilleurs délais. C’est pourquoi ils ne téléchargent pas d’images sur des pages; cela prend trop de bande passante et ralentit l'exploration.

Cette idée (je pense la plus élégante et la plus facile à mettre en œuvre) utilise le mod_rewrite pour masquer le code dans un fichier .jpg/.png /… un fichier image. Cette image doit être disponible sur chaque page que vous souhaitez protéger: il peut s'agir du site Web de votre logo, mais vous choisirez une image de petite taille (car cette image ne doit pas être mise en cache).

Idée:

1/Ajoutez ces lignes à votre .htaccess 

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

2/Créez votre logo.php avec la sécurité

<?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/Incrémentez votre no_logo_count sur chaque page pour laquelle vous devez renforcer la sécurité et vérifiez s’il a atteint votre limite.

Exemple de code:

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

Contrôle des cookies

Vous pouvez créer des cookies du côté javascript pour vérifier si vos utilisateurs interprètent le javascript (un robot utilisant Curl ne le fait pas, par exemple).

L'idée est assez simple: c'est à peu près la même chose qu'une vérification d'image.

  1. Définissez une valeur de $ _SESSION sur 1 et incrémentez-la à chaque visite
  2. si un cookie (défini dans JavaScript) existe, définissez la valeur de la session sur 0
  3. si cette valeur atteint une limite, bannissez votre utilisateur

Code:

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

Protection contre les mandataires

Quelques mots sur les différents types de mandataires que nous pouvons trouver sur le Web:

  • Un proxy «normal» affiche des informations sur la connexion de l'utilisateur (notamment son adresse IP)
  • Un proxy anonyme n'affiche pas l'adresse IP, mais donne des informations sur l'utilisation du proxy sur l'en-tête.
  • Un proxy hautement anonyme n'affiche pas l'adresse IP de l'utilisateur et n'affiche aucune information qu'un navigateur peut ne pas envoyer.

Il est facile de trouver un proxy pour connecter un site Web, mais il est très difficile de trouver des proxies hautement anonymes. 

Certaines variables $ _SERVER peuvent contenir des clés spécifiquement si vos utilisateurs sont derrière un proxy (liste exhaustive tirée de cette question ):

  • CLIENT_IP
  • Renvoyé
  • 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

Vous pouvez donner un comportement différent (limites inférieures, etc.) à vos titres anti-analyse si vous détectez l'une de ces clés dans votre variable $_SERVER.


Conclusion

Il existe de nombreuses façons de détecter les abus sur votre site Web. Vous trouverez donc certainement une solution. Mais vous devez savoir précisément comment votre site Web est utilisé, afin que vos titres ne soient pas agressifs envers vos utilisateurs "normaux". 

95
Alain Tiemblo

Vous pouvez détecter cURL-Useragent par la méthode suivante. Mais soyez averti que l'utilisateur peut être écrasé par l'utilisateur, mais que les paramètres par défaut peuvent être reconnus par:

function is_curl() {
    if (stristr($_SERVER["HTTP_USER_AGENT"], 'curl'))
        return true;
}
0
Fusca Software

Le moyen d'éviter les faux référents est de suivre l'utilisateur

Vous pouvez suivre l'utilisateur avec une ou plusieurs de ces méthodes:

  1. Enregistrez un cookie dans le client du navigateur avec un code spécial (ex: dernière URL visitée, un horodatage) et vérifiez-le à chaque réponse de votre serveur.

  2. Comme auparavant, mais en utilisant des sessions au lieu de cookies explicites

Pour les cookies, vous devez ajouter une sécurité cryptographique telle que.

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

le hachage est calculé dans PHP de cette manière

$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
Maks3w