Собаки с собственным API с ограничением скорости

Обзор:

Моя компания разработала API с ограничением скорости. Наша цель двоякая:

  • A: Создайте сильную экосистему разработчика вокруг нашего продукта.
  • B: продемонстрируйте силу нашего API, используя его для управления нашим собственным приложением.

Уточнение: почему ограничение по скорости вообще?

Мы оцениваем ограничение нашего API, потому что мы продаем его как дополнение к нашему продукту. Анонимный доступ к нашему API имеет очень низкий порог для вызовов API в час, тогда как нашим платным клиентам разрешено более 1000 вызовов в час или более.

Проблема:

Наш API с ограничением скорости отлично подходит для экосистемы разработчика, но для того, чтобы мы могли его дозировать, мы не можем позволить ему ограничиваться одним и тем же ограничением скорости. Передним интерфейсом нашего API является JavaScript, что делает прямые вызовы Ajax API.

Итак, вопрос:

Как вы защищаете api, чтобы ограничение скорости можно было удалить, когда в процессе удаления такого ограничения скорости нельзя легко подделать?

Исследуемые решения (и почему они не работали)

  • Проверьте реферер против заголовка хоста. - Недостаток, потому что referrer легко подделывается.

  • Используйте HMAC для создания подписи на основе запроса и общего секрета, а затем проверьте запрос на сервере. - Недостаток, потому что секрет и алгоритм можно легко определить, посмотрев на JavaScript-интерфейс.

  • Прокси-запрос и подписать запрос в прокси-сервере - все еще ошибочно, поскольку сам прокси предоставляет API.

Вопрос:

Я смотрю на блестящие умы на Stack Overflow, чтобы представить альтернативные решения. Как бы вы решили эту проблему?

Ответ 1

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

Это часто делается с токенами доступа для каждого пользователя, в отличие от одного токена для всего приложения. Каждый предел токена должен быть достаточным для типичного использования вашего API, но ограничивать его тем, кто пытается его нарушить. Например, 100 вызовов в минуту могут быть более чем достаточными для поддержки обычного просмотра, но если я хочу очистить вас, я не смогу эффективно использовать этот бюджет.

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

Ответ 2

Если это вызывает у вас проблему, это вызовет проблему, связанную с вашей предполагаемой экосистемой разработчиков (например, когда они попытаются разработать альтернативный интерфейс). Если вы действительно едите свою собачью пищу, сделайте API (и ограничение скорости) работать для вашего приложения. Вот несколько советов:

  • Не устанавливайте ограничение по IP-адресу. Скорее, ограничение скорости на что-то, связанное с пользователем, например. их идентификатор пользователя. Примените ограничение скорости на этапе аутентификации.

  • Создайте свой API, чтобы пользователям не требовалось постоянно его вызывать (например, дать вызов списка, который возвращает много результатов, а не повторный вызов, который возвращает каждый элемент каждый раз)

  • Создайте веб-приложение с теми же ограничениями, которые вы ожидаете от своей экосистемы разработчика, т.е. убедитесь, что вы можете спроектировать его в разумных пределах дросселирования.

  • Убедитесь, что ваш задний конец является масштабируемым (желательно горизонтально), поэтому вам не нужно вводить дросселирование на уровнях с низким уровнем, что на самом деле вызывает проблему с пользовательским интерфейсом.

  • Убедитесь, что ваше дросселирование имеет возможность справляться с очередями, а также ограничивать более длительное злоупотребление.

  • Убедитесь, что ваше дросселирование выполняет разумные действия с учетом жестокого обращения, которое вы пытаетесь удалить. Например, рассмотрите возможность очередности или задержки умеренных лиц, а не отказ от соединения. Большинство веб-интерфейсов сразу открывают сразу четыре одновременных соединения. Если вы задерживаете попытку открыть пятую, вы попадете только в тот случай, когда они используют CLI одновременно с веб-клиентом (от двух веб-клиентов). Если вы задерживаете n-й вызов API без разрыва, а не с его отказом, конечный пользователь увидит, что ситуация замедляется, а не прерывается. Если вы комбинируете это только с одновременным вызовом N API-вызовов, вы поражаете людей, которые распараллеливают большое количество вызовов API, что, вероятно, не является поведением, которое вы хотите - например. 100 одновременных вызовов API, тогда пробел в течение часа обычно намного хуже, чем 100 последовательных вызовов API за час.

Разве это не отвечало на ваш вопрос? Ну, если вам действительно нужно делать то, что вы просите, ограничение скорости на этапе аутентификации и применять другой предел скорости, основанный на группе, в которую входит ваш пользователь. Если вы используете один набор учетных данных (используемых вашими разработчиками и командой QA), вы получаете более высокий предел скорости. Но вы сразу можете понять, почему это неизбежно приведет вас к тому, что ваша экосистема увидит проблемы, которые ваши команды разработчиков и разработчиков не видят.

Ответ 3

Купите свой продукт. Станьте платным клиентом.

"Анонимный доступ к нашему API имеет очень низкий порог для вызовов API в час, тогда как нашим платным клиентам разрешено более 1000 вызовов в час или более".

Это также помогает тестировать систему с точки зрения клиента.

Ответ 4

К сожалению, идеального решения нет.

Общий подход обычно заключается в том, чтобы предоставить клиентам возможность идентифицировать себя (например, идентификатор, версию и ключ API), например, для клиентов, чтобы регистрировать информацию о себе, которая может использоваться для ограничения доступа (например, клиент является сервером в заданном диапазоне IP-адресов, поэтому разрешает только вызывающим абонентам в этом диапазоне, например, клиент является JavaScript, но доставляется только в определенную категорию браузера, поэтому разрешает доступ к HTTP-запросам, которые определяют определенные строки пользовательского агента; и т.д.), а затем использовать распознавание машин/распознавание образов для обнаружения аномального использования, которое, вероятно, является поддельным клиентом, а затем отклонить трафик от этих поддельных клиентов (или подтвердить с клиентами, что эти обычаи действительно не исходят от законного клиента, замените их поддельные учетные данные, а затем запретить дальнейший трафик с использованием более старых поддельных учетных данных).

Вы можете сделать несколько сложнее обмануть, используя несколько уровней ключа. Например, вы выдаете более долговечный учет, который живет на сервере (и который может использоваться только в ограниченном наборе диапазонов IP-адресов), чтобы сделать вызов API, который записывает информацию о клиенте (например, пользовательский агент) и возвращает более короткоживущий клиентский ключ, который синдицирован в JavaScript для использования на клиенте для клиентских запросов API. Это также несовершенно (spoofer может выдавать один и тот же вызов сервера, чтобы получить учетные данные), но будет сложнее, если возвращенный ключ API будет включен в обфускацию (и часто меняющийся) JavaScript или HTML (что затруднит чтобы надежно извлечь из ответа). Это также дает возможность более легко обнаружить спуфинг; клиентский ключ теперь привязан к конкретному клиенту (например, конкретному агенту пользователя, возможно, даже конкретному банку cookie), что позволяет легко повторить использование другого клиента, а истечение также ограничивает продолжительность повторного использования поддельного ключа.

Ответ 5

Предполагая, что приложение, о котором идет речь, должно быть открыто открыто, у вас нет выбора:

Выберите другой способ продемонстрировать мощь вашего API.. Например, напишите такое приложение и поделитесь его источником, но фактически не запустите этот код. Убедитесь, что он хорошо документирован, так что каждый может развернуть его и увидеть, как он работает (при условии дросселирования).

Запустившееся приложение необходимо будет реорганизовать, чтобы избежать запросов API на стороне клиента и быть более обработанным сервером. Вы все еще можете использовать ваш API, но не очевидным образом - сделайте защищенные запросы на беззатратный API со стороны сервера.

Отрегулируйте ограничение скорости, чтобы ваше приложение могло работать и инвестировать в оптимизацию производительности для обработки нагрузки.

И да, у вас есть основной API без дроссельной заслонки, и держите его внутри частной сети. Дроссель в отдельном общедоступном слое.

Ответ 6

Можете ли вы поддержать отдельный экземпляр пользовательского интерфейса и без дросселя API, а затем ограничить доступ к IP-адресам, поступающим из вашей организации?

Например, разверните все, что стоит за вашим корпоративным брандмауэром, и приложите приложение к той же базе данных, что и открытый экземпляр, если вам нужно обмениваться данными между экземплярами.

Ответ 7

Вы можете попытаться создать уникальный идентификатор сеанса, связанный с определенным IP-адресом/пользователем и ограниченным временем для жизни. Когда пользователь загружает внешний код приложения, код JavaScript вводит сгенерированный идентификатор сеанса в исходный код JavaScript. Идентификатор сеанса будет прикреплен к каждому запросу вашего API и лимит скорости будет снят.

Идентификатор нельзя просто скопировать для подмены, поскольку он действителен только для одного IP-адреса, пользователя и ограниченного количества времени. Таким образом, противник должен будет вызвать вашу страницу и отфильтровать ключ из вашего источника JavaScript или перехватить запрос Ajax каждый раз, когда новый пользователь захочет его использовать.

Другой вариант:

Настройте прокси-сервер для собственного приложения и используйте обфускацию. Ajax просит прокси использовать разные имена из реальных вызовов API, а прокси-сервер переводит их обратно. Таким образом, ваше приложение не будет вызывать getDocument в вашем реальном API, но оно вызовет getFELSUFDSKJE на ваш прокси. Прокси-сервер переведет этот вызов обратно в getDocument и перенаправит его на фактический API с ограничением скорости.

Ваш фактический API не будет ограничивать запросы прокси.

И чтобы другие люди не использовали ваш прокси для своего приложения, вы ежедневно меняете схему обфускации. Обфусканные имена вызовов могут быть сгенерированы автоматически в исходном коде JavaScript и настроены в прокси.

Клиент, желающий использовать это, также должен идти в ногу с вашей изменяющейся обфускацией, чтобы использовать ваш прокси. И вы по-прежнему можете использовать referrer-заголовки и аналогичные для ведения журнала, чтобы вы могли найти людей, использующих ваш прокси-сервер. Или поймать их при изменении схемы обфускации.

Ответ 8

  • IP-адреса источника белого списка
  • Используйте VPN, члены VPN-списка whitelist
  • Прокси-решение или аддон браузера, который добавляет заголовки HTTP, должны быть в порядке, если вы можете защитить прокси-сервер и не обеспокоены тем, что MITM атакует трафик
  • Любое решение, включающее секреты, может смягчить последствия утечек, ежедневно разворачивая секреты.

Ответ 9

Настройте несколько учетных записей и выберите один из них наугад при каждом запросе или измените тот, который вы используете каждый час или около того. Таким образом, вы можете распределить нагрузку на n учетные записи, давая вам до n раз более высокие пределы.

Будьте осторожны, чтобы случайно закрыть себя, если вы пытаетесь найти других пользователей, которые это делают, если это не разрешено для клиентов.