Вы разворачиваете 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 и обнаружение потенциально опасных действий являются частью модуля управления идентификацией. Для более широкой модели безопасности — реестра, карты доступа, архитектуры коллекции с приоритетом чтения — см. безопасность.