Перейти к содержимому

Машинный перевод. Авторитетным источником является английская версия; проверка носителем языка ещё не выполнена.

WIF

Эфемерные учетные данные для Claude Code с федерацией идентичности рабочих нагрузок

Автор Olivares AI 8 мин чтения

Вы разворачиваете Claude Code для команды платформы. Вы настраиваете федерацию идентичности рабочей нагрузки Anthropic так, чтобы каждая сессия обменивалась подтвержденным OIDC утверждением на краткоживущий токен. Проверка безопасности завершена успешно: нет статических ключей, идентичность для каждой сессии, токены с истечением срока действия. На следующее утро один инженер добавляет export ANTHROPIC_API_KEY=sk-ant-... в свой профиль оболочки, потому что скрипту это нужно. Теперь федерация молча обходится для каждой сессии, которую запускает этот инженер. Нет ошибок, нет предупреждений, нет записей в журнале. Статический ключ имеет приоритет, и путь с подтверждением никогда не вызывается.

Это не теоретическое условие гонки. Это задокументированный порядок разрешения учетных данных Anthropic, и это самый распространенный способ, каким развертывания федерации тихо терпят неудачу.

Проблема со статическими ключами

Способ аутентификации Claude Code по умолчанию — это статический API-ключ: строка sk-ant-, установленная как ANTHROPIC_API_KEY. Он работает и обладает тремя свойствами, которые конфликтуют с управлением идентификацией на уровне предприятия.

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

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

Тихий приоритет над федерацией. Это подводный камень. Разрешение учетных данных Anthropic помещает статический ключ (ANTHROPIC_API_KEY, уровень 2) выше пути федерации (уровень 4). Когда оба существуют в одной среде, побеждает статический ключ. Обмен федерацией никогда не выполняется. Ошибка не возникает. Рабочая нагрузка выполняется успешно под идентичностью статического ключа, и все предположения о управлении, основанные на федерации — идентичность в рамках сессии, подтвержденные утверждения, срок действия токена — тихо аннулируются.

Даже пустая переменная (ANTHROPIC_API_KEY="") занимает свое место в порядке приоритета. Аутентификация не удастся, но это все равно предотвратит доступ среды выполнения к пути федерации. Режим сбоя — «ошибка аутентификации», а не «переход к федерации».

Как работает федерация идентичности рабочей нагрузки

WIF заменяет статический ключ на обмен: в ход идет проверенное утверждение, на выходе получается краткоживущий токен. Утверждение представляет собой JWT — либо JWT-SVID от поставщика идентичности SPIFFE, либо стандартный токен OIDC от любого издателя, которому доверяет организация Anthropic.

Обмен следует стандарту RFC 7523 (JWT выдача по держателю). Рабочая нагрузка предъявляет свое утверждение к токен-эндпоинту Anthropic вместе с тремя идентификаторами: какое правило федерации использовать (fdrl_), от какого сервисного аккаунта действовать (svac_) и к какой организации относится обмен. В коде ядро обмена представляет собой POST с JSON-телом:

type exchangeRequest struct {
    GrantType        string `json:"grant_type"`         // "urn:ietf:params:oauth:grant-type:jwt-bearer"
    Assertion        string `json:"assertion"`          // проверенный JWT-SVID или токен OIDC
    FederationRuleID string `json:"federation_rule_id"` // fdrl_...
    OrganizationID   string `json:"organization_id"`
    ServiceAccountID string `json:"service_account_id"` // svac_...
    WorkspaceID      string `json:"workspace_id,omitempty"`
}

Ответ представляет собой ответ токена RFC 6749. Созданный токен имеет префикс sk-ant-oat (OAT = OAuth токен доступа), объявленную область действия (workspace:developer для полного неадминистративного доступа к API или org:manage_tunnels для управления туннелем MCP) и срок действия от 60 секунд до 24 часов.

Токена обновления нет. Когда созданный токен истекает, рабочая нагрузка должна повторно представить своё утверждение и снова выполнить обмен. Это сделано намеренно: само утверждение заверено (проверено на верхнем уровне API рабочей нагрузки SPIFFE или провайдером OIDC), поэтому каждый новый обмен является повторной аттестацией. Компрометированный токен полезен только в течение оставшегося срока действия, и нет пути обновления, который злоумышленник мог бы использовать для его продления.

Результат — идентичность на каждую сессию. Каждая сессия Claude Code обменивается собственной утверждением, получает собственный краткоживущий токен и аутентифицируется как отдельный субъект. Журнал аудита фиксирует, какой сервисный аккаунт действовал, к какому правилу федерации он привязан, в каком рабочем пространстве.

Обнаружение проблемы static-key-shadows-federation

Развернуть WIF недостаточно. Вам также нужно обнаруживать случаи, когда что-то в среде тихо обходит его. Коннектор проверяет среду выполнения на наличие статических учетных данных (ANTHROPIC_API_KEY или ANTHROPIC_AUTH_TOKEN) и проверяет, используется ли одновременно федерация (либо через объявленные правила федерации, либо через сигнал ANTHROPIC_IDENTITY_TOKEN_FILE). Когда выполняются оба условия, он выдает предупреждение высокой серьезности о соблюдении правил управления.

func (s *Source) detectShadowing(at time.Time) (model.FindingReport, bool) {
    _, hasKey := s.envLookup(envAPIKey)
    _, hasAuth := s.envLookup(envAuthToken)
    if !hasKey && !hasAuth {
        return model.FindingReport{}, false
    }
    _, hasTokenFile := s.envLookup(envIdentityTokenFile)
    federationInUse := len(s.federation) > 0 || hasTokenFile
    if !federationInUse {
        return model.FindingReport{}, false
    }
    // ...
    return model.FindingReport{
        Kind:     "governance",
        Severity: model.SeverityHigh,
        Title:    "Static Anthropic key shadows Workload Identity Federation",
        // DetailHash определяет, КАКАЯ переменная имеет приоритет — значение не записывается
    }, true
}

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

Статический ключ без использования федерации — это просто статический ключ, — коннектор его не отмечает. Срабатывание находки происходит только когда существуют оба, потому что именно в такой конфигурации оператор считает, что федерация активна, хотя это не так.

Цикл согласования: заявленное против фактического

Обнаружение проблемы со статическим ключом — это половина картины. Другая половина заключается в проверке того, что сама конфигурация федерации не изменилась. Коннектор поддерживает объявленную базовую линию — правила федерации, которые оператор явно определяет как управляемые — и сравнивает её с текущим состоянием конфигурации организации Anthropic WIF.

Текущее состояние получается из трёх конечных точек WIF Admin API:

  • GET /v1/organizations/service_accounts — сервисные аккаунты (svac_), на которые направлены правила федерации
  • GET /v1/organizations/federation_issuers — издатели OIDC/SPIFFE (fdis_), которым организация доверяет
  • GET /v1/organizations/federation_rules — правила (fdrl_), связывающие издателей с сервисными аккаунтами

Эти конечные точки требуют маркера носителя org:admin OAuth — отдельного удостоверения, отличного от ключа Admin API sk-ant-admin, который используют считыватели списка. Admin API WIF явно отклоняет ключи Admin API, поэтому коннектор использует отдельного аутентифицированного клиента для сверки.

Разница между заявленными и фактическими данными выявляет семь категорий находок:

Случай расхожденияЧто это значитСтепень важности
undeclared_live_ruleФактическое правило, которое оператор никогда не объявлял или не управлялВысокая
declared_rule_not_liveОбъявленное правило, которое больше не существует на стороне источникаСредняя
scope_driftФактическая область отличается от объявленнойСредняя (высокая, если расширена на всю организацию)
lifetime_driftСрок действия живого токена расходился с объявленнымСредний (Высокий, если дольше заявленного)
over_broad_subjectПравило в реальном времени без реального ограничения субъектаСредний
orphan_ruleПравило, ссылающееся на отсутствующего эмитента или служебную учетную записьСредний
orphan_issuerЭмитент, на который не ссылается ни одно правилоНизкий

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

Когда токен org:admin не настроен, процесс сверки просто не выполняется. Коннектор работает только с объявленным базовым уровнем и честен в отношении своего охвата: он никогда не подделывает активный список. Когда доступ к живому API невозможен (сетевая ошибка, истечение срока действия токена), он выдает единственное обнаружение reconciliation_unavailable и продолжает работу — предоставление списка и обнаружение потенциально опасных действий не должны быть связаны с состоянием токена org:admin.

Чтение в первую очередь и минимальные данные

Коннектор соблюдает строгий контракт минимизации данных. Каждый вызов API является GET. Он никогда не создает, не обновляет и не удаляет объект Anthropic. Он передает только метаданные идентификации: идентификаторы, имена, электронные адреса, роли, подсказки ключей. Никогда секрет ключа. Никогда закрытый ключ. Никогда созданный токен в состоянии покоя.

Созданный токен из обмена WIF возвращается вызывающему и никогда не логируется, не сохраняется и не передается коннектором. Единственной записью, которая попадает в реестр управления, является структура ExchangeAudit — которая сознательно не содержит токена:

type ExchangeAudit struct {
    FederationRuleID string
    OrganizationID   string
    ServiceAccountID string
    WorkspaceID      string
    Scope            string
    TokenType        string
    ExpiresAt        time.Time
}

Такая же минимизация применяется к живой сверке. Когда коннектор считывает конфигурацию JWKS издателя федерации, он сокращает ответ до двух булевых значений: режим обнаружения (discovery, explicit_url или inline) и прикреплён ли пользовательский сертификат CA. Встроенный материал JWK — открытые ключи, которые громоздки и никогда не нужны для решений по управлению — никогда не декодируется в сохранённое или выдаваемое поле. PEM-сертификат CA декодируется только для получения флага присутствия и сразу же отбрасывается.

Условия CEL на правила федерации используются для анализа состояния (выражение CEL правила является частью его границы безопасности), но они никогда не оцениваются. Коннектор не зависит от движка CEL — оценка является отдельной задачей.

Что это позволяет

Статические ключи смешивают аутентификацию с идентичностью. Каждая сессия — это один и тот же субъект, каждый токен живет вечно, а наличие ключа в dotfile тихо отключает любую федерацию, которая, как вы думали, защищала вас.

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

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


Коннектор идентификации, обмен WIF и обнаружение потенциально опасных действий являются частью модуля управления идентификацией. Для более широкой модели безопасности — реестра, карты доступа, архитектуры коллекции с приоритетом чтения — см. безопасность.

Похожие материалы

Часто задаваемые вопросы

Должен ли статический ключ содержать действительный API-ключ, чтобы дублировать федерацию?

Нет. Приоритет учетных данных Anthropic основан на наличии, а не на их действительности. Пустой ANTHROPIC_API_KEY="" получает свой приоритетный слот точно так же, как и заполненный ключ: он находится выше уровней федерации в порядке разрешения, поэтому среда выполнения никогда не переходит к аттестованному пути. Коннектор определяет это, проверяя, установлен ли переменная окружения (os.LookupEnv), а не на то, непуста ли ее значение. Пустой ключ не пройдет аутентификацию, но он так же полностью затеняет федерацию, как и настоящий.

Что произойдет, если у меня нет токена org:admin OAuth для живой сверки?

Разъём работает без этого. Он точно моделирует то, что оператор объявляет в конфигурации федерации, генерирует управляемый реестр NHI и разрешённые грантовые связи из этих деклараций и выполняет обнаружение ошибок с использованием статического ключа. Он просто пропускает проход живой сверки, потому что конечные точки WIF Admin API отклоняют всё, кроме токена носителя org:admin OAuth. Вы получаете базовый уровень только с декларациями, который честно отображает своё покрытие: он никогда не создаёт фиктивный живой реестр.

Узнайте, до чего могут добраться ваши агенты

Olivares AI — открытая self-hosted платформа для управления вашим парком AI. Разверните её на собственной инфраструктуре и получите карту доступа, которую давно запрашивают ваши команды безопасности и платформ.