如何检测假用户(爬虫)和curl
其他一些网站使用cURL和假http引用来复制我的网站内容。 我们有没有办法检测cURL或不是真正的networking浏览器?
没有魔术解决scheme来避免自动抓取。 每个人都可以做,机器人也可以做。 只有解决办法才能使工作变得更加困难,以至于只有技术娴熟的极客才会尝试通过。
几年前,我遇到了麻烦,我的第一个build议是,如果你有时间,自己做一个爬虫(我假设爬虫是抓取你的网站的人),这是最好的学校。 通过抓取几个网站,我学到了不同types的保护措施,通过关联他们,我一直保持高效。
我给你一些保护你可能会尝试的例子。
每IP的会话
如果用户每分钟使用50个新会话,则可以认为该用户可能是不处理Cookie的搜寻器。 当然,curl可以完美地pipe理cookies,但是如果您将它与每个会话的访问计数器相结合(稍后解释),或者如果您的抓取工具是一个不重要的cookie事务,则可能是有效的。
很难想象有50个共享同一个连接的人会同时在你的网站上获得(这当然取决于你的stream量,这取决于你)。 如果发生这种情况,您可以locking您的网站页面,直到validation码被填充。
想法:
1)你创build2个表格: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是否被禁止(你设置了一个标志为真)
4)如果不是的话,你要数他有多less次他的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页吗?
想法:
- 在用户会话中创build一个数组,它将包含访问时间()。
- 删除此arrays中超过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" />'; } ?>
一个图像下载
当一个爬虫需要做他的肮脏的工作,这是大量的数据,并在最短的时间。 这就是为什么他们不在网页上下载图片。 它占用了太多的带宽,使爬行速度变慢。
这个想法(我认为最elegent和最容易实现)使用mod_rewrite隐藏在.jpg / .png / …图像文件中的代码。 这个图像应该在你想保护的每个页面上都可用:它可以是你的logo网站,但是你会select一个小尺寸的图像(因为这个图像不能被caching)。
想法:
1 /将这些行添加到您的.htaccess
RewriteEngine On RewriteBase /tests/anticrawl/ RewriteRule ^logo\.jpg$ logo.php
2 /创build您的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检查
您可以在JavaScript一侧创buildcookie来检查您的用户是否parsingjavascript(例如,使用Curl的爬网程序不会)。
这个想法很简单:这与图像检查大致相同。
- 将$ _SESSION值设置为1,并在每次访问中增加它
- 如果存在cookie(用JavaScript设置),请将session值设置为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" />'; }
防止代理
关于我们可能通过networkingfind的不同types的代理的一些话:
- “正常”代理显示有关用户连接的信息(特别是他的IP)
- 匿名代理不显示IP,但提供有关头上的代理使用情况的信息。
- 高匿名代理不显示用户IP,也不显示任何浏览器不能发送的信息。
很容易find连接任何网站的代理,但很难find高匿名代理。
一些$ _SERVERvariables可能包含关键字,特别是如果你的用户在代理之后(详尽的列表来自这个问题 ):
- CLIENT_IP
- 转发
- 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
variables中的某个键,您可能会向您的反爬行证券提供不同的行为(下限等)。
结论
有很多方法来检测您的网站上的滥用行为,所以你会find一个肯定的解决scheme。 但是你需要知道你的网站是如何使用的,所以你的证券不会对你的“普通”用户有侵略性。
记住:HTTP并不神奇。 每个HTTP请求都有一组定义的头文件; 如果这些头文件是通过networking浏览器发送的,那么它们也可以通过任何程序发送 – 包括cURL(和libcurl)。
有些人认为这是一个诅咒,但另一方面,这是一个祝福,因为它大大简化了Web应用程序的functiontesting。
更新:正如unr3al011正确地注意到的那样,curl不执行JavaScript,所以理论上可以创build一个页面,当它被抓取器查看时会有不同的行为(例如,通过设置,稍后用JS方法检查特定的cookie)。
不过,这将是一个非常脆弱的防守。 该页面的数据仍然需要从服务器抓取 – 这个HTTP请求(并且总是 HTTP请求)可以通过curl来模拟。 检查这个答案例如如何击败这样的防守。
…我甚至没有提到一些抓取器能够执行JavaScript。 )
避免假引荐者的方式是跟踪用户
您可以通过以下一种或多种方法跟踪用户:
-
使用一些特殊的代码(例如:上次访问的url,时间戳)在浏览器客户端中保存一个cookie,并在服务器的每个响应中对其进行validation。
-
和以前一样,但使用会话而不是显式cookie
对于cookie,您应该添加像这样的encryption安全性。
[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的build议,但是对于其他的grabber / donwloaders来说并不适用。
我build议你尝试build立一个bot陷阱 ,你可以处理可以执行JavaScript的抓取器/下载器。
我不知道任何解决scheme,以完全防止这一点,所以我最好的build议是尝试多种解决scheme:
1)只允许已知的用户代理,如所有主stream浏览器在您的.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