Подтвердить что ты не робот

Как отключить пользователей API сайта?

Легальные пользователи моего сайта иногда забивают сервер запросами API, которые вызывают нежелательные результаты. Я хочу установить ограничение не более, чем сказать один вызов API каждые 5 секунд или n вызовов в минуту (пока не определился с точным ограничением). Я мог бы зарегистрировать каждый вызов API в БД и выполнить расчет по каждому запросу, чтобы убедиться, что он превысил лимит, но все эти дополнительные накладные расходы при каждом запросе будут побеждать цель. Какие другие менее ресурсоемкие методы я мог бы использовать, чтобы установить лимит? Я использую PHP/Apache/Linux, для чего это стоит.

4b9b3361

Ответ 1

Хорошо, нет возможности делать то, что я просил, без записи на сервер, но я могу, по крайней мере, исключить протоколирование каждого отдельного запроса. Одним из способов является использование метода дросселирования "просачивающегося ковша", где он отслеживает только последний запрос ($last_api_request) и отношение количества запросов/ограничений для временного интервала ($minute_throttle). Непроницаемое ведро никогда не сбрасывает счетчик (в отличие от дросселя API Twitter, который сбрасывается каждый час), но если ковш становится заполненным (пользователь достигает предела), они должны ждать n секунд, чтобы ведро немного опорожнилось, прежде чем они смогут сделать другой запрос. Другими словами, это как предел прокатки: если в течение периода времени есть предыдущие запросы, они медленно вытекают из ведра; это только ограничивает вас, если вы заполняете ведро.

Этот фрагмент кода будет вычислять новое значение $minute_throttle для каждого запроса. Я указал минуту в $minute_throttle, потому что вы можете добавлять дроссели в течение любого периода времени, например, ежечасно, ежедневно и т.д.... хотя более одного из них быстро начнут запутывать пользователей.

$minute = 60;
$minute_limit = 100; # users are limited to 100 requests/minute
$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds
$last_api_diff = time() - $last_api_request; # in seconds
$minute_throttle = $this->get_throttle_minute(); # get from the DB
if ( is_null( $minute_limit ) ) {
    $new_minute_throttle = 0;
} else {
    $new_minute_throttle = $minute_throttle - $last_api_diff;
    $new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;
    $new_minute_throttle += $minute / $minute_limit;
    $minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute  );
    # can output this value with the request if desired:
    $minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;
}

if ( $new_minute_throttle > $minute ) {
    $wait = ceil( $new_minute_throttle - $minute );
    usleep( 250000 );
    throw new My_Exception ( 'The one-minute API limit of ' . $minute_limit 
        . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );
}
# Save the values back to the database.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );

Ответ 2

Вы можете контролировать скорость с помощью алгоритма маркера токена, что сопоставимо с алгоритмом негерметичного ковша. Обратите внимание, что вам придется разделить состояние ведра (т.е. Количество токенов) над процессами (или любой объем, который вы хотите контролировать). Поэтому вы можете подумать о блокировке, чтобы избежать условий гонки.

Хорошая новость: я сделал все это для вас: bandwidth-throttle/token-bucket

use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$storage = new FileStorage(__DIR__ . "/api.bucket");
$rate    = new Rate(10, Rate::SECOND);
$bucket  = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);

if (!$bucket->consume(1, $seconds)) {
    http_response_code(429);
    header(sprintf("Retry-After: %d", floor($seconds)));
    exit();
}

Ответ 3

Я не знаю, жив ли этот поток или нет, но я бы предложил сохранить эти статистические данные в кеше памяти, например memcached. Это уменьшит накладные расходы на ведение журнала запроса в БД, но все еще будет служить цели.

Ответ 4

Самое простое решение - просто дать каждому ключу API ограниченное количество запросов в течение 24 часов и reset их в известное фиксированное время.

Если они исчерпывают свои запросы API (т.е. счетчик достигает нуля или предел, в зависимости от направления, которое вы подсчитываете), прекратите подавать их данные, пока не найдете reset их счетчик.

Таким образом, в их интересах будет не забивать вас запросами.

Ответ 5

Вы говорите, что "все лишние накладные расходы при каждом запросе будут побеждать цель", но я не уверен, что это правильно. Разве это не цель предотвращения взлома вашего сервера? Вероятно, это способ реализовать, так как это требует только быстрого чтения/записи. Вы даже можете обработать проверки сервера API на другой DB/диск, если вы беспокоитесь о производительности.

Однако, если вы хотите альтернативы, вы должны проверить mod_cband, сторонний модуль apache, предназначенный для помощи в регулировании полосы пропускания. Несмотря на то, что в основном это ограничение пропускной способности, он также может работать на основе запросов в секунду. Я никогда не использовал его, поэтому я не уверен, какие результаты вы получите. Был еще один модуль, называемый mod-throttle, но этот проект, похоже, теперь закрыт и никогда не был выпущен для чего-либо выше серии Apache 1.3.

Ответ 6

В дополнение к реализации с нуля вы также можете взглянуть на инфраструктуру API, такую ​​как 3scale (http://www.3scale.net), которая ограничивает скорость, как а также множество других вещей (аналитика и т.д.). Для него есть плагин PHP: https://github.com/3scale/3scale_ws_api_for_php.

Вы также можете использовать что-то вроде Varnish infront API и ограничивать скорость API.