Показывать капчу при обнаружении неожиданной навигации, чтобы предотвратить злоупотребление трафиком
Я заметил, что некоторые пользователи перегружают мой веб-сайт, загружая несколько файлов (например, 500 файлов одновременно) и открывая больше страниц за небольшую продолжительность, я хочу показать капчу , если пользователь обнаружит неожиданную навигацию.
Я знаю, как реализовать Капчу , но я не могу понять, каков наилучший подход для обнаружения злоупотреблений трафиком с использованием (PHP)?
3 answers
Общий подход заключается в использовании чего-то вроде memcached для хранения запросов на минутной основе, у меня есть небольшой класс с открытым исходным кодом, который достигает этого: php-ratelimiter
Если вас интересует более подробное объяснение того, почему запросы должны храниться на минутной основе, проверьте это сообщение.
Итак, подводя итог, ваш код может выглядеть следующим образом:
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;
}
Введение
На аналогичный вопрос был дан ответ до Предотвращение затопления PHP-скрипта , но это может быть недостаточной причиной:
- Он использует
$_SERVER["REMOTE_ADDR"]
, и они являются некоторым общим соединением, имеют одинаковоеPublic IP Address
- Существует так много
Firefox addon
, что can позволяет пользователям использовать несколько прокси-серверов для каждого запроса
Многократный запрос! = Многократная загрузка
Предотвращение множественного запроса полностью отличается от многократной загрузки почему?
Чтобы не представить файл 10MB
, для загрузки которого потребуется 1min
, если вы ограничите пользователей, чтобы сказать 100 request per min
, что это означает, что вам предоставлен доступ к пользователю для загрузки
10MB * 100 per min
Чтобы устранить эту проблему, вы можете посмотреть Загрузка - максимальное количество подключений на пользователя?.
Множественный запрос
Вернуться к доступу к странице вы можете использовать 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;
}
}
Заключение
Это базовое доказательство концепции, и к нему могут быть добавлены дополнительные слои, такие как
- Установите другой предел для разных URL-адресов
- Добавьте поддержку штрафов, когда вы блокируете пользователя на определенное количество минут или часов
- Обнаружение и Другой предел для
Tor
соединений - и т.д.
Я думаю, что в этом случае вы можете использовать сеансы. Инициализируйте сеанс, чтобы сохранить метку времени [используйте microtime для достижения лучших результатов], а затем получите метку времени новой страницы.Разница может быть использована для анализа частоты посещаемых страниц и отображения капчи.
Вы также можете запустить счетчик посещаемых страниц и использовать 2d-массив для хранения страницы и метки времени.Если значение посещаемых страниц внезапно увеличивается, вы можете проверить разницу во времени.