Могут ли алиасы API-хостов обойти проверку назначения?
Алиасы API-хостов могут скрывать непроверенные назначения. Узнайте, как учитывать CNAME и базовые URL, привязывать учётные данные к authority и предотвращать утечки через перенаправления.

Проверка назначения имеет смысл лишь тогда, когда проверяется имя, получающее учётные данные. Если команда одобрила https://api.example.test, но позволяет агентам использовать https://api-us.example.test, https://gateway.example.test или предоставленный поставщиком endpoint для совместимости, одобрение описывает намерение, а не границу.
Я видел, как это ломается самым скучным способом: кто-то одобряет знакомое имя хоста, переменная развёртывания указывает на региональный алиас, и тот же bearer-токен работает. Никакой эффектной атаки не требовалось. У команды просто оказалось больше имён назначений, чем признавала её процедура проверки.
Первое исправление касается самой модели. CNAME-запись, альтернативный базовый URL, перенаправление, IP-адрес и HTTP authority связаны между собой, но это не одно и то же. Если считать их одинаковыми, проверки выглядят тщательными, а средства контроля пропускают учётные данные.
Проверка назначения охватывает только буквальный хост
Проверка api.example.test не одобряет api-eu.example.test, proxy.example.test или api.example.test.evil.invalid. Учётные данные должны отправляться только точному нормализованному имени хоста, которое явно указано в инвентаризации для этих учётных данных.
Команды часто начинают с неформального правила вроде «этот токен предназначен для API Example». Это утверждение о владельце, а не правило назначения. Компания может использовать много доменов, поставщик может направлять трафик через несколько имён, а третья сторона может обслуживать часть пограничной инфраструктуры поставщика. HTTP-клиенту нужен конкретный URL. Средству контроля он тоже нужен.
Опишите границу в понятиях, которые может сравнить URL-парсер:
- схема: обычно
https - имя хоста: ASCII-имя в нижнем регистре после обработки IDNA
- порт: явно указанный порт либо порт по умолчанию для схемы
- правило для пути: только если учётные данные ограничены определённой API-поверхностью
Не заменяйте список имён хостов проверкой суффикса. endsWith("example.test") пропустит notexample.test. endsWith(".example.test") всё равно пропустит любой нынешний и будущий поддомен. Это может быть приемлемо для внутренней сервисной сети с одним владельцем и строгим контролем выдачи имён. Для учётных данных, способных менять production-данные, такой подход обычно слишком небрежен.
Неловкий вопрос в том, должен ли человек одобрять каждое имя хоста до использования. Для токена с высокими правами - да. Для токена с узкими правами, который работает с поставщиком, публикующим множество региональных endpoints, одобрите поддерживаемый список и добавляйте в него имена только осознанным изменением. Стоимость ещё одной проверки меньше, чем объяснение, почему токен попал на хост, которого никто не зафиксировал.
Это также отделяет контроль назначения от проверки содержимого запроса. Рецензент может согласиться с GET к одобренному хосту и отклонить POST, меняющий биллинг. Это разные вопросы. Не утверждайте, что проверка имени хоста определяет безопасность запроса. Она определяет, куда можно отправлять учётные данные.
CNAME меняет маршрут, а не HTTP-хост
CNAME меняет DNS-разрешение. Сам по себе он не переписывает имя хоста в URL, HTTP-заголовок Host или TLS SNI, которое отправляет обычный HTTPS-клиент.
Предположим, агент вызывает этот URL:
https://api.example.test/v1/orders
DNS может ответить так:
api.example.test. 300 IN CNAME api.edge.vendor.test.
api.edge.vendor.test. 300 IN A 203.0.113.42
TCP-соединение попадёт на 203.0.113.42, возможно, в инфраструктуру поставщика. Но клиент всё равно должен запросить в TLS api.example.test и отправить Host: api.example.test. Если сервер не предъявит сертификат, действительный для исходного имени, проверка сертификата должна завершиться ошибкой. Если предъявит, сервер уполномочен завершать трафик для этого имени, по крайней мере в этот момент.
Это различие опровергает популярное, но неверное решение: одобрять цель CNAME так, будто это API authority. Целевое имя - свидетельство маршрутизации. Оно помогает понять, куда идёт трафик, но не заменяет проверку имени хоста из URL, которое получает заголовок авторизации.
RFC 1034 определяет CNAME как алиас другого доменного имени и требует продолжить поиск по каноническому имени. Это объясняет поведение резолвера, но не решение об отправке HTTP-учётных данных. DNS ничего не знает о bearer-токенах, API scope или одобрении изменений.
CNAME становится значимым для безопасности в четырёх практических случаях:
- DNS-именем владеет другая команда или поставщик, поэтому смена записи может изменить место, где завершается трафик для одобренного authority.
- Разрешённый адрес попадает в неожиданную сеть, например во внутренний диапазон или по адресу метаданных облака.
- Средство контроля одобряет имена из DNS-вывода вместо URL authority и подменяет реальное решение об авторизации отношением алиаса.
- Приложение строит второй URL из разрешённого имени, цели перенаправления или результата service discovery, а затем пересылает учётные данные.
В четвёртом случае токены утекают. Первые три ослабляют проверку и повышают вероятность будущей утечки. Для них нужны разные тесты и разные владельцы.
CNAME также не защищает от DNS rebinding. Если разрешённое клиентом имя хоста позже начнёт указывать на другой адрес, клиент может создать новое соединение с этим адресом. Для назначений под вашим контролем отслеживайте записи и ограничивайте допустимые адреса. Для назначений вне вашего контроля не считайте, что однократная DNS-проверка доказывает постоянную безопасность.
Альтернативные базовые URL создают более тихий обход
Альтернативные базовые URL чаще становятся обходом, потому что напрямую меняют HTTP authority. Эти имена скрываются в переменных окружения, настройках SDK по умолчанию, тестовых фикстурах и примечаниях к миграции.
Сервис может по законным причинам документировать все эти варианты:
https://api.example.test
https://api-us.example.test
https://sandbox-api.example.test
https://gateway.example.test/service-a
https://tenant-42.api.example.test
Сегодня они могут завершаться на одной и той же пограничной инфраструктуре. Это не означает, что им подходят одни и те же учётные данные. Production-endpoint может принимать токен уровня учётной записи, sandbox-endpoint может отправлять запросы в отдельную систему, путь шлюза может выбирать другой сервис, а имя хоста tenant может маршрутизироваться по идентичности клиента. Имена содержат эксплуатационные различия, которые сравнение IP-адресов скрывает.
Схема сбоя предсказуема. В кодовой базе задан API_BASE_URL со значением production по умолчанию. Разработчик меняет его для регионального теста или миграции. Слой подстановки учётных данных видит URL, который всё ещё кажется связанным с сервисом, и добавляет заголовок. Проверка назначения была привязана к ярлыку вроде «Example API», а не к точному authority, поэтому расширение остаётся незамеченным.
Сначала исправьте модель данных, а потом код. Для каждых учётных данных нужна отдельная запись с четырьмя полями, которые может проверить рецензент:
credential: orders-write-prod
allowed authorities:
https://api.example.test:443
https://api-us.example.test:443
purpose: create and amend production orders
owner: commerce operations
review trigger: DNS change, new endpoint, scope change
Слово «authorities» здесь выбрано намеренно. Храните схему, хост и порт вместе. Хост, допустимый по HTTPS, не одобрен автоматически для нестандартного порта. Путь может иметь значение, когда общий шлюз использует один хост для несвязанных API, но правила для путей требуют аккуратной нормализации и не должны заменять отдельные учётные данные там, где различаются права.
Не считайте опубликованный поставщиком список готовой инвентаризацией. Документация поставщика говорит, что может существовать. Ваш код и настройки развёртывания говорят, что вы можете вызвать. Нужны оба источника, а также имена, оставшиеся в старой автоматизации после миграции.
Аудитория учётных данных задаёт границу
Правильный вопрос не в том, «какие серверы принадлежат этому поставщику?». Нужно спросить: «какие HTTP authorities могут получить этот секрет в этом конкретном пути запроса?»
У bearer-токена нет встроенного ограничения аудитории, если его не обеспечивает выдающая сторона. Как только клиент помещает его в заголовок Authorization, каждый получатель заголовка может попытаться его использовать. У базовой аутентификации и собственных заголовков с API-ключами та же проблема на уровне передачи. Шифрование транспорта защищает запрос в пути, но не сужает аудиторию на уровне приложения.
OAuth access tokens иногда содержат claim aud. Он помогает resource server отклонить токен, предназначенный для кого-то другого, но не путайте отклонение на стороне сервера с безопасным поведением клиента. Отправка токена не тому хосту всё равно раскрывает его этому хосту и помещает в его журналы доступа, телеметрию или очередь инцидентов. Отклонённый токен лучше принятого, но это всё равно утечка, которой можно было избежать.
Привязка учётных данных состоит из двух частей:
- Клиент подставляет учётные данные только для проверенного authority.
- Выдающая сторона даёт им максимально узкие практичные scope, аудиторию и окружение.
Нужны обе части. Привязка к хосту не даёт ошибке клиента разбросать секрет по соседним сервисам. Scope ограничивает ущерб, если скомпрометированы ожидаемый хост, его журналы или настройка маршрута.
Поэтому единый API-ключ на всю организацию - плохая сделка. Он упрощает настройку и усложняет реакцию на инцидент. Если один ключ попадает в payments API, сборщик аналитики и staging-шлюз, нельзя отозвать доступ к одному назначению, не нарушив работу всех трёх. Отдельные учётные данные превращают ошибку маршрутизации в локальную ротацию, а не в аварию для всей команды.
Соберите имена из кода, DNS и документации поставщика
Инвентаризация endpoints заслуживает доверия, когда отражает наблюдаемое использование, а не только запланированную архитектуру. Соберите её из кода, конфигурации развёртывания, DNS и документации поставщика, затем устраните расхождения.
Начните с поиска по репозиторию URL-схем и настроек базовых URL. Проверьте код приложения, shell-скрипты, определения CI, файлы примеров, определения инфраструктуры и тестовые фикстуры. Зафиксируйте каждое имя хоста, даже устаревшее на вид. Старые имена остаются опасными, если их всё ещё вызывает задача cron или prompt агента.
Затем разрешите каждого кандидата в том же контексте резолвера, который использует вызывающая машина. На macOS или другой Unix-системе базовая проверка выглядит так:
dig +noall +answer api.example.test CNAME A AAAA
Полезный вид вывода:
api.example.test. 300 IN CNAME api.edge.vendor.test.
api.edge.vendor.test. 60 IN A 203.0.113.42
api.edge.vendor.test. 60 IN AAAA 2001:db8::42
Если ваш резолвер не возвращает всю цепочку в одном ответе, выполните отдельные запросы для CNAME, A и AAAA. Запишите дату запроса и использованный резолвер, потому что split DNS может выдавать разные ответы ноутбуку, CI runner и production-хосту. Не переносите полученный IP-адрес в постоянный список разрешённых адресов для интернет-API. Адреса пограничной инфраструктуры регулярно меняются. Сохраняйте их как свидетельства для проверки и предупреждайте о неожиданных изменениях.
Затем создайте таблицу инвентаризации: одна строка на authority, а не на поставщика. Она должна без созвона отвечать на такие вопросы:
| Authority | Кто использует | Учётные данные | Владелец DNS | Ожидаемый маршрут | Статус проверки |
|---|---|---|---|---|---|
https://api.example.test:443 | production-агент | orders-write-prod | поставщик | публичная edge-инфраструктура | одобрено |
https://api-us.example.test:443 | региональная задача | orders-write-us | поставщик | публичная edge-инфраструктура | ожидает проверки |
https://gateway.example.test:443 | устаревший скрипт | нет | внутренняя команда | внутренний шлюз | выведено из эксплуатации |
Не добавляйте строку только потому, что у поставщика есть такой endpoint. Помечайте его как неиспользуемый, пока он не понадобится коду, конфигурации или одобренной миграции. Инвентаризация должна показывать случайно доступные возможности, а не документировать все варианты.
OWASP Server-Side Request Forgery Prevention Cheat Sheet высказывает близкую мысль: когда приложение обращается только к известным доверенным приложениям, список разрешённых адресов жизнеспособен, но одной проверки домена недостаточно для понимания поведения DNS. Эти рекомендации относятся к SSRF, но практический вывод применим и здесь. Список имён хостов надёжен, только если вы знаете, кто поддерживает эти имена, куда они разрешаются и может ли вызывающий превратить проверенное имя в другой запрос позже.
Перенаправления требуют отдельной проверки
Перенаправление - новое решение о назначении. Клиент, автоматически следующий перенаправлениям, может покинуть одобренный authority после первого запроса, а заголовок авторизации не должен отправляться вслед за ним.
Для API-вызовов с учётными данными начните с отключённых перенаправлений. Считайте ответ 301, 302, 303, 307 или 308 ответом, требующим явного решения. Если новый URL уже есть в точном списке authorities для этих учётных данных, а поведение метода и тела запроса приемлемо, выполните к нему отдельный запрос. Если его нет в списке, остановитесь.
Коды статуса не взаимозаменяемы. 303 обычно меняет запрос на GET, а 307 и 308 сохраняют метод и тело запроса. Автоматическое повторение POST с заголовком авторизации имеет более серьёзные последствия, чем безобидный на вид GET, особенно когда новый хост отличается.
Проверьте поведение конкретной HTTP-библиотеки, которую используете. Некоторые клиенты удаляют чувствительные заголовки при смене хоста, другие сохраняют их при неожиданных условиях, а обёртки могут переопределять значения по умолчанию. Тест должен перехватить запрос, пришедший на контролируемый второй хост, и подтвердить, что он не получил ни учётные данные, ни скопированное тело. Не считайте название библиотеки доказательством её политики перенаправлений.
Безопасный путь вызова легко описать:
1. Parse the requested URL.
2. Normalize and match its authority against the credential record.
3. Inject the credential only after that match.
4. Send one request with redirects disabled.
5. If a redirect arrives, parse and review the new authority before any new request.
Эта последовательность защищает и от более тонкой ошибки: подстановки заголовка в универсальный клиент до проверки назначения. Когда код прикрепляет токен к повторно используемому объекту запроса, последующие изменения URL могут перенести его в непредусмотренное место. Привязывайте учётные данные в последний ответственный момент, когда окончательный URL уже известен.
Общим именам хостов нужны отдельные учётные данные
Одно имя хоста может обслуживать несколько API, окружений и tenants. Точное сопоставление хоста необходимо, но оно не выражает все различия безопасности за общим шлюзом.
Рассмотрим https://gateway.example.test. Один путь может создавать счета, другой отправлять телеметрию, а третий администрировать пользователей. Если один bearer-токен авторизует все три, проверка хоста даёт лишь грубую защиту. Ошибка, меняющая /telemetry на /admin, остаётся на одобренном хосте и всё равно сработает.
Обычно правильный ответ - отдельные учётные данные с разными scope. Дайте клиенту телеметрии токен, который не может администрировать пользователей, даже если оба вызова идут к одному хосту. Если поставщик поддерживает аудитории или resource indicators, используйте их. Если он поддерживает только широкие токены, разделите service accounts или выберите более безопасную границу интеграции, вместо того чтобы делать вид, будто список разрешённых путей решает проблему авторизации.
Правила для путей всё же полезны. Они могут ловить ошибки в программировании и делать намерение понятным при проверке. Но с путями проще ошибиться, чем с хостами: percent encoding, повторяющиеся слеши, сегменты с точками, переписывание шлюзом и перенаправления версий усложняют сравнение. Нормализуйте путь тем же URL-парсером и библиотекой запросов, которая выполняет вызов. Никогда не принимайте решение безопасности на основе самописной проверки подстроки.
То же относится к портам. api.example.test:443 и api.example.test:8443 - разные authorities. Обратный прокси может направлять их в разные сервисы, а разработчик, тестирующий второй порт, может решить, что первая проверка покрывает и его. Зафиксируйте оба или не разрешайте ни один.
Размещайте одобрения там, где человек ещё видит назначение
Одобрение со словами «разрешить API-вызов» просит рецензента подписать пустой чек. В запросе нужны метод, полный нормализованный authority, путь запроса, идентификатор учётных данных и вызывающий процесс. Иначе у человека нет практической возможности заметить, что агент сменил production API на забытый хост для совместимости.
Sallyport хранит учётные данные в зашифрованном хранилище и сам выполняет HTTP-действие, не передавая секрет агенту. Это полезно, потому что одобрение можно разместить на границе действия, где одновременно видны назначение и вызывающий процесс.
Не превращайте экран одобрения в ритуал. Одобрение на сессию подходит для известного короткого запуска агента, который вызывает стабильный набор разрешённых authorities. Для учётных данных, способных перемещать деньги, удалять данные или обращаться к административным endpoints, требуйте подтверждение каждого вызова. Трение должно соответствовать последствиям одного неверного вызова, а не терпению рецензента.
В Sallyport намеренно нет языка политик и движка правил, поэтому он не должен становиться поводом пропустить инвентаризацию endpoints. Его защита хранилища и средства авторизации отвечают на вопрос, может ли процесс использовать учётные данные сейчас. Запись об учётных данных всё равно должна отвечать, какой authority имеет право их получить.
Записи активности должны сохранять достаточно сведений, чтобы позже восстановить решение: запрошенный authority, разрешённый маршрут, если он доступен, метод, статус, запрашивающая сессия и применённая запись учётных данных. Не записывайте секрет или полные чувствительные payloads только ради улучшения аудита. Журнал, создающий второе хранилище секретов, не улучшает аудит.
Повторно проверяйте алиасы после каждого изменения DNS и интеграции
Привязка назначения ослабевает, когда меняются DNS, endpoints поставщика или настройки развёртывания. Сделайте проверку инвентаризации частью этих изменений, а не ежегодной процедурой, которая находит ворох устаревших имён.
Запускайте проверку, когда кто-то добавляет или редактирует CNAME, меняет A- или AAAA-запись разрешённого внутреннего имени, вводит региональный endpoint, заменяет SDK, меняет API gateway или добавляет перенаправление. Рецензент должен сравнить старый и новый списки authorities, а затем решить, могут ли существующие учётные данные следовать за изменением. Смена владельца DNS заслуживает такого же внимания, как смена владельца кода.
Для имён под вашим контролем предупреждайте, когда одобренный хост начинает разрешаться в приватные, loopback, link-local или неожиданные внутренние адреса. Это защита и от SSRF, и от утечки учётных данных. Для публичных имён поставщиков предупреждайте об изменении цели CNAME и существенных изменениях диапазонов адресов, затем расследуйте их, а не блокируйте автоматически каждую ротацию CDN.
Держите небольшой регрессионный тест рядом с интеграцией. Он должен проверить одобренный URL, альтернативный базовый URL вне списка, хост с вводящим в заблуждение суффиксом, явный альтернативный порт и перенаправление на другой хост. Ожидаемый результат - не просто неудачное сетевое подключение. Клиент обязан отказаться прикреплять учётные данные до того, как запрос достигнет назначения вне списка.
Это и есть стандарт, которого стоит придерживаться. Если агент может сначала отправить секрет, а уже потом выяснить, что назначение неверно, проверка не сработала в момент, когда была нужна.
Вопросы и ответы
Меняет ли CNAME хост, который получает API-токен?
CNAME сопоставляет одно DNS-имя с другим во время разрешения. При этом клиент может отправить исходное имя хоста из URL в HTTP-заголовке Host и TLS SNI, поэтому имя цели CNAME не становится автоматически HTTP-назначением.
Опасны ли CNAME-алиасы API?
Нет. Сам по себе алиас не опасен, и многие поставщики используют его для управления трафиком. Риск появляется, когда учётные данные одобрены для одного имени, а клиент может отправить их через другой похожий базовый URL, либо когда владелец DNS может смениться без проверки.
Как ограничить API-учётные данные одобренными хостами?
Для каждой учётной записи используйте точный список имён хостов и сверяйте с ним каждый настроенный базовый URL. Не заменяйте этот список родительским доменом, проверкой суффикса или диапазоном IP-адресов.
Какие хосты включать в инвентаризацию API-endpoints?
Отдельно перечислите production-, sandbox-, региональные, tenant-, устаревшие, прокси- и приватные endpoints. Затем проверьте конфигурационные файлы, переменные развёртывания, документацию поставщика, DNS-записи и поведение HTTP-перенаправлений, чтобы найти реально используемые имена.
Может ли HTTP-перенаправление передать bearer-токен другому хосту?
Перенаправление может изменить URL-назначение после первого запроса. Безопасный клиент удаляет чувствительные заголовки при перенаправлении на другой хост, но для запросов с учётными данными лучше отключить автоматические перенаправления, пока вы явно не проверите каждый переход.
Достаточно ли проверки TLS-сертификата для проверки API-назначения?
Нет. Сертификат доказывает, что сервер контролирует имя в момент подключения. Он не говорит, должны ли конкретные учётные данные отправляться этому имени и не гарантирует, что DNS-запись позже не укажет на другой адрес.
Стоит ли нескольким сервисам использовать один API-ключ на одном домене?
Если у сервисов и окружений разные права, общим шлюзам нужны отдельные учётные данные. Список разрешённых хостов не исправит токен, который даёт каждой нагрузке доступ к одной слишком широкой учётной записи.
Для API-доступа проверять DNS-имена или IP-адреса?
Сначала проверяйте URL authority, затем разрешайте A-, AAAA- и CNAME-записи, чтобы понять маршрут. Повторяйте обе проверки при изменении DNS, миграции поставщика или запуске нового регионального endpoint.
Почему *.example.com - плохой список разрешённых API-назначений?
Нет. Точные хосты разрешают запланированное имя, например api.example.com, а wildcard также пропустит dev.api.example.com, old.api.example.com и любое будущее имя в этой зоне. У таких имён часто разные владельцы и механизмы контроля.
Как проверить, может ли агент обойти одобрение назначения?
Полезная проверка показывает, может ли агент выполнить запрос с учётными данными к хосту вне списка, перейти туда по перенаправлению или использовать неучтённый базовый URL. Если может, проверка фиксирует намерение, но не обеспечивает его выполнение.