Безопасность API интернет-магазина: аутентификация, ограничение запросов, CORS, валидация ввода и защита от уязвимостей
Почему «включённый HTTPS» не равен безопасности API
Многие владельцы сайтов считают HTTPS синонимом безопасности: раз трафик между браузером и сервером зашифрован, значит, с интерфейсом всё в порядке. Это первое заблуждение: HTTPS решает проблему «подслушивания в пути», но не решает проблему «легальную личность используют для флуда», «вредоносный скрипт массово бьёт по вашему эндпоинту входа», «в запрос вставляют SQL и вытаскивают таблицу».
Контринтуитивный вывод здесь такой:Настоящие проблемы интернет-магазинов почти всегда связаны с уязвимостями прикладного уровня, а не транспортного. Слив базы, удаление базы, зафлуженные эндпоинты — в 90% случаев это следствие невалидированного ввода, слабой аутентификации и отсутствия ограничения запросов. WAF отсекает часть известных сигнатур атак, но не закрывает дыры в вашей собственной бизнес-логике. Это совпадает с выводами После взлома сайта: сначала чистите код, потом говорите об усилении защиты.
Каждый из пяти слоёв отвечает за свой участок: пропустите один — и целый класс атак пройдёт насквозь. В таблице ниже собрано, что именно закрывает каждый слой и что будет, если его пропустить.
| Слой защиты | Какие атаки блокирует | Типовая реализация | Что будет, если пропустить |
|---|---|---|---|
| Сертификация | Анонимные вызовы, выдача себя за другого | API Key, JWT, OAuth2 | Данные из эндпоинта может вытянуть кто угодно |
| Ограничение запросов | Брутфорс, массовый флуд эндпоинтов | Скользящее окно по IP/Key | Credential stuffing на входе, слив базы |
| CORS | 恶意站点跨域读取数据 | Белый список источников | Чтение данных третьим сайтом под сессией пользователя |
| Валидация ввода | SQL-инъекции, XSS, запись грязных данных | Проверка по белому списку + параметризованные запросы | Слив всей базы, перехват сессии через XSS в админке |
| Сканирование уязвимостей | Известные бэкдоры, устаревшие зависимости | Сканирование зависимостей + регулярный скан уязвимостей | Использование старого плагина с публичным EXP |
Аутентификация: как выбрать из трёх способов и как управлять ключами
Внешние интерфейсы интернет-магазина делятся на два типа: для фронтенда браузера (сессия входа, отправка форм) и для интеграции с внешними системами (Webhook, синхронизация с третьими сервисами). Для каждого типа используются свои способы аутентификации — не путайте их.
Для авторизации на фронтенде используйте JWT: у токена есть срок действия, по умолчанию 2 часа, после истечения обменивается на новый через refresh token. Для интеграции с внешними системами используйте долгосрочный API Key и передавайте его в заголовке запроса, а не в параметрах URL — URL попадают в логи и историю браузера. OAuth2 нужен только когда требуется «разрешить пользователю авторизовать третью сторону для доступа от его имени». В 90% сценариев одиночного сайта он не нужен и лишь добавляет сложности.
Критерий: если интерфейс обслуживает только ваш собственный фронтенд, достаточно JWT; если его будут вызывать программы партнёров, выдайте отдельный API Key и создавайте ключи отдельно для каждого партнёра, чтобы можно было в любой момент отозвать конкретный. Меняйте ключи раз в квартал, при ротации старый и новый ключи сосуществуют 7 дней, чтобы интеграция партнёра не отвалилась сразу.
| Способ авторизации | Когда подходит | Срок действия токена | Ротация / отзыв |
|---|---|---|---|
| Session Cookie | Традиционный вход через форму в админку | Сессия браузера | Недействителен после выхода |
| JWT | Разделение фронтенда и бэкенда, вызовы API с фронтенда | Access-токен 2 часа | refresh 7 дней, чёрный список на сервере |
| API Key | Внешние системы, Webhook-колбэки | Долгосрочный, выдаётся по партнёрам | Ротация раз в квартал, можно отозвать отдельно |
| OAuth2 | Стороннее приложение действует от имени пользователя | Короткий access + refresh | Пользователь может в любой момент отозвать доступ |
III. Ограничение частоты запросов: по кому ограничивать и какие пороги задавать
Самая частая ошибка при ограничении частоты — «один порог на весь сайт». Нормальная частота вызовов у интерфейса входа и у интерфейса списка товаров различается на порядок: с одним порогом вы либо бьёте по живым людям, либо не останавливаете атаку. Правильный подход — задавать уровни по интерфейсам и ограничивать по разным измерениям.
Три измерения ограничения складываются: по IP — чтобы один атакующий не забил канал, по аккаунту — чтобы перебор паролей по словарю не крутился бесконечно, по API Key — чтобы программа одного партнёра не вышла из-под контроля. Пороги должны быть конкретными числами; таблица ниже — стартовые значения для типичных интерфейсов одиночного сайта, после запуска их калибруют по реальному трафику.
Пример из практики: Интерфейс входа атакуют перебором. Настройте 10 попыток в минуту по IP и 5 неудачных попыток в минуту по аккаунту; при превышении лимита в течение 5 минут подряд блокируйте IP на 1 час. Это эффективнее простого «блокировать аккаунт после 5 неудачных входов», потому что атакующий пробует один пароль к разным аккаунтам, и порог по одному аккаунту его не остановит. О борьбе со спамом в формах читайте в Надёжность формы запроса— там описаны подходы к защите от спама.
| Тип интерфейса | Измерение ограничения | Стартовый порог | Действие при превышении |
|---|---|---|---|
| Вход / восстановление пароля | IP + аккаунт | 10 раз/минуту | Временная блокировка на 1 час |
| Отправка формы запроса | IP | 3 раза/минуту | Показать капчу |
| Публичный список товаров | IP | 60 раз/минуту | Вернуть 429 |
| Внешняя интеграция по API | API Key | QPS по условиям договора | 429 + уведомить партнёра |
| Поиск по сайту | Сессия | 20 раз/минуту | Вернуть 429 |
IV. CORS: когда включать и в каких пределах
CORS (Cross-Origin Resource Sharing) — это встроенная защита браузера: по умолчанию скрипты вашей страницы не могут читать ответы чужих API с другого домена. Дело не в том, что “включил — и стало небезопасно”, а в том, что “настроил неправильно — и стало небезопасно”. Самая частая ошибка — из лени прописать Access-Control-Allow-Origin: * вместе с Allow-Credentials: true. Это равносильно тому, чтобы распахнуть кросс-доменный доступ всему интернету.
Правило простое: если ваш API вызывается только с вашего собственного фронтенда, в белый список CORS вносите только свой домен (в двух вариантах — с www и без), без *. Если API нужен стороннему фронтенду, добавляйте домены по одному, не используйте размытые шаблоны вроде *.yourdomain.com: стоит кому-то зарегистрировать или взломать поддомен — и он сразу становится вектором атаки.
Публичным API без Cookie (например, списку товаров) учётные данные CORS вообще не нужны: браузер не так строг к простым запросам, а чем меньше настроек — тем меньше поверхностей риска. О том, где проходит граница между учётными данными и соответствием требованиям при передаче данных за рубеж, читайте в разделе Комплаенс-основа для выхода на зарубежные рынки о Cookie и трансграничной передаче данных.
| Ситуация | Настройка Allow-Origin | Allow-Credentials | Безопасно? |
|---|---|---|---|
| Вызывается только своим фронтендом | Белый список строго по своему домену | true (нужна сессия) | Безопасность |
| Публичный API с данными о товарах | Не настраивать или * | false | Безопасность |
| Из лени написать * + credentials true | * | true | Опасно, нужно исправить |
| Шаблон *.yourdomain.com | Размытое сопоставление | true | Опасно: взлом поддомена всё ломает |
| Фронтенды нескольких партнёров | Белый список по каждому домену | true | Безопасно, регулярно чистите лишнее |
Пять. Валидация входных данных и защита от уязвимостей: не пускаем грязь на порог
Валидация входных данных — самый часто пропускаемый и самый чреватый последствиями слой из пяти, когда работаешь один. Принцип один:никогда не доверяйте данным, приходящим с фронтенда и из внешних API. Каждый входной параметр проверяйте по белому списку — тип, длину, формат, допустимые значения. Не совпало — сразу отказ, не пытайтесь “очистить и использовать дальше”.
SQL-инъекции устраняются параметризованными запросами — не склеивайте SQL-строки вручную. XSS устраняется экранированием вывода: сохраняйте пользовательский контент в базу без экранирования, а при выводе на страницу экранируйте по контексту (правила для HTML, JS и URL различаются). При загрузке файлов ограничьте белый список расширений, переименовывайте файлы и храните их вне веб-каталога. Это базовые пункты OWASP Top 10, никаких “продвинутых приёмов” здесь нет.
Сканирование уязвимостей — в постоянном графике: файлы блокировки зависимостей проверяйте автоматически раз в неделю; при обнаружении критичных уязвимостей (CVE выше 9 баллов) обновляйте или временно отключайте функцию в течение 72 часов; раз в месяц проводите пассивное сканирование всего сайта и по результатам удаляйте устаревшие плагины. Как настроить мониторинг и оповещения, читайте в разделе Мониторинг сайта и оповещения. О компромиссах по безопасности самого стека технологий — в разделе Технологический стек crater.
| Мера защиты | Как сделать | Частота / порог | Красная линия |
|---|---|---|---|
| Валидация входных данных | Тип + длина + белый список значений | Обязательно для каждого API | Запрещено проверять только на фронтенде |
| SQL-инъекции | Параметризованные запросы / подготовленные выражения | Обязательная проверка на код-ревью | Запрет на склейку SQL из строк |
| XSS | Экранирование вывода по контексту | Все точки вывода пользовательских данных | Запрет на вывод rich text без очистки |
| Загрузка файлов | Белый список расширений + переименование | Обязательно для всех эндпоинтов загрузки | Запрет на выполнение скриптов в папке загрузок |
| Сканирование зависимостей на уязвимости | Автоматическое сканирование lock-файлов | Раз в неделю | Критические уязвимости от 9 баллов — исправить за 72 часа |
FAQ: частые вопросы
Обязателен ли WAF для безопасности самостоятельного сайта?
Что выбрать: JWT или API Key?
Как задать пороги ограничения частоты, чтобы не задеть обычных пользователей?
Чем плохо Access-Control-Allow-Origin: * в CORS?
Зачем валидировать входные данные на бэкенде, если это уже сделано на фронтенде?
Как часто проводить сканирование на уязвимости?
В заключение
Суть безопасности API на независимом сайте не в наборе инструментов, а в эшелонировании: если один уровень обошли, следующий ещё держит удар. Одиночная защита — самое хрупкое место: как бы красиво вы ни настроили WAF, без валидации входных данных инъекция всё равно дойдёт до базы. Из пяти уровней в первую очередь делайте валидацию входных данных и аутентификацию — это минимальные вложения с максимальной отдачей. Ограничение частоты запросов и CORS — вопрос настройки, на них уйдёт полдня. А сканирование уязвимостей превратите в еженедельную автоматическую задачу.
Три шага к внедрению. Первый: добавьте аутентификацию на все внешние интерфейсы, долгосрочные ключи выдавайте отдельно каждому партнёру. Второй: настройте многоуровневое ограничение частоты запросов для входа и форм, а на публичных интерфейсах начните с мягких лимитов. Третий: проверяйте каждый входной параметр на бэкенде по белому списку и подключите сканирование зависимостей к еженедельным задачам. После этих трёх шагов ваши интерфейсы будут сталкиваться с «редкими попытками прощупать», а не с «открытыми настежь дверями». С ритмом повседневного обслуживания можно свериться графиком обслуживания сайта и спланировать вместе.
Запомните одну фразу: безопасность API — это не одна стена, а пять дверей. Запереть нужно каждую: пропустите одну — и остальные четыре бесполезны.
