Когда владелец панели думает о защите, он думает о внешней атаке: нагрузка, перебор паролей, сканеры уязвимостей. Но у панели с открытым API есть вторая дверь, и выглядит она совершенно легально: запросы приходят с валидным ключом, от вашего же зарегистрированного клиента, и от нормальной работы ничем не отличаются. Разбираем, что реселлер может сделать вашей панели одной строкой curl, какие проверки это останавливают и почему прятать кнопки бесполезно.
Атака, у которой есть ваш ключ
Разница с обычной атакой принципиальная, и от неё зависит всё остальное. Внешнюю отбивают на подступах: фильтр трафика, лимит попыток входа, капча - об этом есть отдельный разбор про безопасность панели. Здесь наоборот: трафик легитимный, ключ настоящий, клиент реальный, и ни один фильтр на входе такой запрос не отклонит.
Злого умысла при этом чаще всего нет. На практике источников три:
- Реселлер, который автоматизировал работу небрежно. Его скрипт опрашивает статусы каждого заказа каждые десять секунд, ретраит по кругу при любой ошибке и дублирует отправку, если ответ пришёл с задержкой. Он не атакует, он просто не подумал.
- Клиент, который щупает границы. Заказ на единицу товара, заказ на миллион, пустая ссылка, чужой номер заказа в
status. Обычно это любопытство, иногда разведка. - Тот, кому достался чужой ключ. Ключ утёк из репозитория реселлера, из его скриншота или от бывшего сотрудника. Для панели это по-прежнему «ваш клиент».
Главное правило: интерфейс ничего не защищает
На этом спотыкаются чаще всего. Спрятать кнопку, убрать услугу из витрины, отключить раздел - всё это меняет только то, что видит браузер. Клиент с ключом в браузер не заходит вовсе.
В нашем ядре правило закреплено тремя отдельными серверными проверками, и в комментарии к каждой стоит одна и та же мысль: без неё запрет обходился бы одной строкой curl.
- Скрытая услуга остаётся заказуемой. Гео-ограничение, снятие с витрины, флаг «скрыта» - это про отображение. Проверять надо на сервере, в момент оформления, иначе услуга, которую вы убрали из каталога для определённой страны, спокойно уйдёт в заказ по прямому запросу.
- Отключённое действие остаётся вызываемым. Если вы запретили клиентам отмену или доливку, кнопка исчезнет из кабинета, но метод API никуда не денется. Права должны проверяться в том же месте, где выполняется действие.
- Заблокированный клиент остаётся с ключом. Блокировка закрывает вход, но ключ у него на руках прежний. Если API не спрашивает статус аккаунта, блокировка обходится за секунду.
И четвёртое, самое общее. Все проверки заказа - доступность услуги, границы объёма, подтверждение почты, тип услуги, хватает ли денег - должны жить в одном месте, через которое проходят все входы: и форма витрины, и API, и гостевой чекаут. Разводить их по вызывающим нельзя: рано или поздно один вход примет то, что другой отклоняет, и разойдутся они молча.
Фейковые и мусорные заказы
Второй слой - заказы, которые формально валидны, но ничего вам не приносят. Каждый такой уходит поставщику, тратит его лимиты и ваше время на разбор.
- Дубли на одну ссылку. Самая частая беда с кривым скриптом реселлера: ответ пришёл с задержкой, скрипт решил, что заказ не создался, и отправил его ещё раз. Лечится флагом на уровне услуги: не принимать второй заказ на ту же ссылку той же услуги, пока прежний ещё в работе. Важная деталь - завершённый и отменённый заказы блокировать не должны: повторный заказ после выполнения совершенно легитимен, и запрет на него обернётся тикетом.
- Объём за границами. Минимум и максимум услуги обязаны проверяться на сервере. Отдельно - кратность: если поставщик принимает только сотнями, заказ на 250 он отклонит, а деньги у вас уже списаны.
- Порции drip-feed. Тут ошибаются даже аккуратные интеграторы. Под границы услуги должна попадать порция, то есть объём одного прогона, а не общая сумма заказа. Проверка по общему объёму пропустит заказ, который поставщик развалит на первом же запуске.
- Неподходящий тип услуги. У «своих комментариев» количество считается по числу строк, у пакетных услуг цена фиксирована за заказ, а подписку по объёму не считают вовсе. Если API принимает
quantityдля всех типов подряд, расчёт суммы разойдётся с тем, что посчитает поставщик. Что за типы и какие у них параметры - в справочнике по методам API.
Выжигание лимитов
Третий слой самый неприятный, потому что бьёт не по одному клиенту, а по всем сразу. Скрипт, опрашивающий статусы в цикле без пауз, съедает общий бюджет запросов, и вместе с ним встают остальные реселлеры.
Ориентиры по числам. У нас клиентский API ограничен сотней с небольшим запросов в минуту, админский - примерно втрое выше, и это не непоследовательность: клиентским ключом обычно работает человек или небольшой скрипт, а в админский ходит робот и пачками. У панелей на распространённом движке предел ещё жёстче - около пяти запросов в секунду на панель, и наткнуться на него в реальной интеграции проще, чем кажется.
Два следствия, о которых редко думают заранее:
- Лимит общий, а виноват один. Если счётчик считает по адресу, а не по ключу, несколько реселлеров из одной сети делят один бюджет. Один неаккуратный скрипт положит работу остальным, и жаловаться придут все.
- Чужие лимиты тоже ваши. Панель ходит к поставщику по его правилам, и шквал заказов от одного клиента выжигает ваш лимит у поставщика. Дальше его API начнёт отвечать отказами на всё подряд, а разбираться будете вы.
Правильный ответ - не запрет, а пакетные методы и внятная документация: опрос статусов пачкой до сотни номеров решает ту же задачу, что цикл по одному заказу, но в сто раз дешевле для обеих сторон.
Что делать с ключами
- Ключ должен быть длинным и случайным. Шестьдесят с лишним символов из криптографического генератора. Ключ, выведенный из id клиента или его почты, подбирается перебором.
- Перевыпуск - кнопкой, а не через поддержку. Утечка ключа не редкость, и если её устранение требует переписки, клиент просто не станет ничего делать.
- Ключ не должен возвращаться наружу. Ни в ответах API, ни в выгрузках, ни в списке клиентов в админке. Показали один раз - дальше только перевыпуск.
- Только HTTPS. Ключ передаётся параметром запроса, и по открытому протоколу он виден целиком любому промежуточному узлу.
Как это вообще увидеть
Главная проблема злоупотребления изнутри в том, что оно не выглядит как проблема: заказы создаются, деньги списываются, панель работает. Минимум, который стоит иметь:
- Пометка источника у заказа. Заказ, оформленный ключом, должен отличаться от оформленного в витрине. Без этого вы не ответите даже на вопрос, какая доля оборота идёт через API.
- Раскладка по клиентам. Один реселлер, дающий девяносто процентов запросов и два процента выручки, виден только в разрезе. Что ещё смотреть владельцу регулярно - в разборе метрик панели.
- Отказы, а не только успехи. Всплеск ответов «неверный ключ» - это перебор. Всплеск «неверная услуга» - чаще всего чей-то скрипт ходит по устаревшему каталогу.
Итог
Открытый API - это не дыра, а главный способ роста панели: реселлер с ключом приносит объём, которого вручную не набрать. Просто защищать его надо не тем, чем защищают сайт. Все правила серверные, все входы проходят через одни и те же проверки, лимиты объявлены заранее, ключ перевыпускается в одно нажатие. Спрятанная кнопка не защищает ничего. Кому и зачем выдавать ключ - в статье про API панели для реселлеров.
Посмотреть, как это работает на живой панели, можно без затрат: заказы через наш собственный каталог не тарифицируются вообще, а по сторонним поставщикам бесплатны первые 100 заказов в месяц. Ключ выдаётся сразу, и прогнать по нему свой скрипт можно в первый же день.