我注意到有些用户通过下载多个文件(例如同时500个文件)来重载我的网站并在较短的时间内打开更多页面,如果用户检测到意外导航,我想显示验证码
我知道如何实施 Captcha ,但我无法弄清楚使用(PHP)检测流量滥用的最佳方法是什么?
答案 0 :(得分:5)
一种常见的方法是使用像memcached这样的东西来存储请求,我已经开源了一个实现这个目标的小类:php-ratelimiter
如果您有兴趣更详细地解释为什么需要在一分钟内存储请求,check this post。
总而言之,您的代码可能最终看起来像这样:
if (!verifyCaptcha()) {
$rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"]);
try {
$rateLimiter->limitRequestsInMinutes(100, 5);
} catch (RateExceededException $e) {
displayCaptcha();
exit;
}
}
实际上,代码是基于每分钟的,但您可以很容易地将其调整为每30秒一次:
private function getKeys($halfminutes) {
$keys = array();
$now = time();
for ($time = $now - $halfminutes * 30; $time <= $now; $time += 30) {
$keys[] = $this->prefix . date("dHis", $time);
}
return $keys;
}
答案 1 :(得分:2)
<强>简介强>
在Prevent PHP script from being flooded之前已经回答了类似的问题,但这可能不是充分的理由:
$_SERVER["REMOTE_ADDR"]
,并且它们是某些共享连接具有相同的Public IP Address
Firefox addon
可以允许用户为每个请求使用多个代理多个请求!=多个下载
防止多个请求与多个下载完全不同?
要想象一个10MB
的文件需要1min
下载,如果你限制用户说100 request per min
这意味着你有权访问用户下载
10MB * 100 per min
要解决此问题,您可以查看Download - max connections per user?。
多个请求
返回页面访问权限,您可以使用SimpleFlood
扩展memcache
来限制每秒用户数。它使用cookies
来解决共享连接问题并尝试获取真实IP地址
$flood = new SimpleFlood();
$flood->addserver("127.0.0.1"); // add memcache server
$flood->setLimit(2); // expect 1 request every 2 sec
try {
$flood->check();
} catch ( Exception $e ) {
sleep(2); // Feel like wasting time
// Display Captcher
// Write Message to Log
printf("%s => %s %s", date("Y-m-d g:i:s"), $e->getMessage(), $e->getFile());
}
请注意,SimpleFlood::setLimit(float $float);
接受浮动,因此您可以
$flood->setLimit(0.1); // expect 1 request every 0.1 sec
使用的课程
class SimpleFlood extends \Memcache {
private $ip;
private $key;
private $prenalty = 0;
private $limit = 100;
private $mins = 1;
private $salt = "I like TO dance A #### Lot";
function check() {
$this->parseValues();
$runtime = floatval($this->get($this->key));
$diff = microtime(true) - $runtime;
if ($diff < $this->limit) {
throw new Exception("Limit Exceeded By : $this->ip");
}
$this->set($this->key, microtime(true));
}
public function setLimit($limit) {
$this->limit = $limit;
}
private function parseValues() {
$this->ip = $this->getIPAddress();
if (! $this->ip) {
throw new Exception("Where the hell is the ip address");
}
if (isset($_COOKIE["xf"])) {
$cookie = json_decode($_COOKIE["xf"]);
if ($this->ip != $cookie->ip) {
unset($_COOKIE["xf"]);
setcookie("xf", null, time() - 3600);
throw new Exception("Last IP did not match");
}
if ($cookie->hash != sha1($cookie->key . $this->salt)) {
unset($_COOKIE["xf"]);
setcookie("xf", null, time() - 3600);
throw new Exception("Nice Faking cookie");
}
if (strpos($cookie->key, "floodIP") === 0) {
$cookie->key = "floodRand" . bin2hex(mcrypt_create_iv(50, MCRYPT_DEV_URANDOM));
}
$this->key = $cookie->key;
} else {
$this->key = "floodIP" . sha1($this->ip);
$cookie = (object) array(
"key" => $this->key,
"ip" => $this->ip
);
}
$cookie->hash = sha1($this->key . $this->salt);
$cookie = json_encode($cookie);
setcookie("xf", $cookie, time() + 3600); // expire in 1hr
}
private function getIPAddress() {
foreach ( array(
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR'
) as $key ) {
if (array_key_exists($key, $_SERVER) === true) {
foreach ( explode(',', $_SERVER[$key]) as $ip ) {
if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
return $ip;
}
}
}
}
return false;
}
}
结论
这是概念的基本证明,可以添加其他图层,例如
Tor
连接的检测和不同限制答案 2 :(得分:1)
我认为你可以在这种情况下使用会话。 初始化会话以存储时间戳[使用microtime获得更好的结果]然后获取新页面的时间戳。可以使用差异来分析被访问页面的频率并显示验证码。
您还可以在被访问的页面上运行计数器,并使用2d数组来存储页面和时间戳。如果正在访问的页面的值突然增加,那么您可以检查时间戳差异。