Перейти до вмісту

Машинний переклад. Авторитетним джерелом є англійська версія; перевірка носієм мови ще не виконана.

WIF

Ефемерні облікові дані для Claude Code з федерацією ідентичностей для робочого навантаження

Автор Olivares AI 8 хв читання

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

Це не теоретична проблема гонки. Це задокументований порядок вирішення облікових даних 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 годин.

Немає токена оновлення. Коли термін дії згенерованого токену закінчується, робоче навантаження має повторно подати своє твердження та пройти обмін знову. Це зроблено навмисно: саме твердження підтверджено (верифіковано вгору по потоку за допомогою SPIFFE workload API або провайдера 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.

Поточний стан отримується з трьох кінцевих точок Admin API WIF:

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

Ці кінцеві точки потребують маркер org:admin OAuth на утримувача — окреме посвідчення від ключа адміністративного API sk-ant-admin, який використовують читачі списку. Адміністративний API WIF явно відкидає ключі адміністративного 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. Він несе лише метадані ідентифікації: ID, імена, електронні адреси, ролі, підказки ключів. Ніколи секрет ключа. Ніколи приватний ключ. Ніколи токен, що зберігається у спокої.

Створений токен із обміну 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-середовища. Розгорніть її на власній інфраструктурі та отримайте карту доступу, про яку давно просять ваші команди безпеки й платформи.