Używasz Claude Code dla zespołu platformy. Konfigurujesz federację tożsamości obciążenia Anthropic, tak aby każda sesja wymieniała poświadczone stwierdzenie OIDC na krótkożyjącą token. Przegląd bezpieczeństwa jest czysty: brak kluczy statycznych, tożsamość przypisana do sesji, tokeny, które wygasają. Następnego ranka jeden z inżynierów dodaje export ANTHROPIC_API_KEY=sk-ant-... do swojego profilu shell, ponieważ skrypt go potrzebuje. Federacja jest teraz cicho omijana dla każdej sesji uruchamianej przez tego inżyniera. Brak błędu, brak ostrzeżenia, brak wpisu w logu. Klucz statyczny ma pierwszeństwo i ścieżka poświadczona nigdy nie jest uruchamiana.
To nie jest teoretyczny stan wyścigu. Jest to udokumentowany porządek rozwiązywania poświadczeń Anthropic i jest to najczęstszy sposób, w jaki wdrożenia federacyjne cicho zawodzą.
Problem z kluczami statycznymi
Domyślny sposób uwierzytelniania Claude Code to statyczny klucz API: ciąg sk-ant- ustawiony jako ANTHROPIC_API_KEY. Działa to i ma trzy cechy, które stoją w konflikcie z zarządzaniem tożsamością w przedsiębiorstwie.
Brak wygaśnięcia, brak sygnału rotacji. Statyczny klucz jest ważny do momentu, gdy ktoś go unieważni. Nie ma wbudowanego okresu ważności, przypomnienia o rotacji ani mechanizmu wymuszającego ponowne uwierzytelnienie. Klucz wydany podczas proof-of-concept może nadal uwierzytelniać obciążenia produkcyjne miesiące później.
Wspólna tożsamość. Każda sesja używająca tego samego klucza uwierzytelnia się jako ten sam podmiot. Ślad audytu pokazuje, które środowisko pracy było używane, ale nie może rozróżnić, który inżynier, która maszyna lub która automatyzacja wykonała konkretne żądanie. Przypisanie do sesji jest strukturalnie niemożliwe.
Cicha przewaga nad federacją. To jest pułapka własna. Rozwiązywanie poświadczeń Anthropic umieszcza statyczny klucz (ANTHROPIC_API_KEY, poziom 2) ponad ścieżką federacji (poziom 4). Gdy oba istnieją w tym samym środowisku, zwycięża klucz statyczny. Wymiana federacyjna nigdy nie jest próbowana. Nie występuje żaden błąd. Obciążenie działa pomyślnie pod tożsamością klucza statycznego, a wszystkie założenia dotyczące zarządzania oparte na federacji — tożsamość ograniczona do sesji, poświadczone stwierdzenia, wygaśnięcie tokenu — są cicho unieważniane.
Nawet pusty zmienny (ANTHROPIC_API_KEY="") zajmuje swoje miejsce w kolejności priorytetów. Uwierzytelnianie nie powiedzie się, ale wciąż uniemożliwi środowisku uruchomieniowemu dotarcie do ścieżki federacji. Tryb błędu to „błąd uwierzytelniania”, a nie „przejście do federacji”.
Jak działa federacja tożsamości obciążenia
WIF zastępuje statyczny klucz wymianą: wchodzi zweryfikowane oświadczenie, wychodzi krótko żyjący token. Oświadczenie jest JWT — albo JWT-SVID od dostawcy tożsamości SPIFFE, albo standardowy token OIDC od dowolnego wydawcy, któremu ufa organizacja Anthropic.
Wymiana odbywa się zgodnie z RFC 7523 (JWT przyznanie typu bearer). Obciążenie prezentuje swoje oświadczenie do punktu końcowego tokenu Anthropic wraz z trzema identyfikatorami: która zasada federacji powinna być dopasowana (fdrl_), jako które konto serwisowe ma działać (svac_) oraz do której organizacji należy wymiana. W kodzie, sedno wymiany to żądanie POST z ciałem w formacie JSON:
type exchangeRequest struct {
GrantType string `json:"grant_type"` // "urn:ietf:params:oauth:grant-type:jwt-bearer"
Assertion string `json:"assertion"` // zweryfikowany JWT-SVID lub token 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"`
}
Odpowiedź jest odpowiedzią tokenową RFC 6749. Wydany token posiada prefiks sk-ant-oat (OAT = OAuth Token Dostępu), zadeklarowany zakres (workspace:developer dla pełnego nieadministracyjnego dostępu do API, lub org:manage_tunnels dla MCP zarządzania tunelem) oraz czas życia między 60 sekundami a 24 godzinami.
Nie ma tokenu odświeżającego. Po wygaśnięciu wydanego tokenu, obciążenie musi ponownie przedstawić swoje poświadczenie i wykonać ponownie wymianę. Jest to celowe: samo poświadczenie jest poświadczone (zweryfikowane przez SPIFFE workload API lub dostawcę OIDC), dlatego każda ponowna wymiana to ponowne poświadczenie. Skompromitowany token jest użyteczny tylko przez pozostały czas życia i nie istnieje ścieżka odświeżania, którą atakujący mógłby wykorzystać do jego przedłużenia.
Wynikiem jest tożsamość per-sesję. Każda sesja Claude Code wymienia własne stwierdzenie, otrzymuje własny, krótkotrwały token i uwierzytelnia się jako odrębny podmiot. Rejestr audytu zapisuje, które konto usługowe działało, powiązane z którą regułą federacji, ograniczoną do którego obszaru roboczego.
Wykrywanie pułapki static-key-shadows-federation
Samo wdrożenie WIF nie wystarczy. Musisz również wykryć, kiedy coś w środowisku cicho je omija. Konektor sprawdza środowisko wykonawcze pod kątem obecności statycznego poświadczenia (ANTHROPIC_API_KEY lub ANTHROPIC_AUTH_TOKEN) i weryfikuje, czy federacja jest używana jednocześnie (albo poprzez zadeklarowane reguły federacji, albo poprzez sygnał ANTHROPIC_IDENTITY_TOKEN_FILE). Gdy oba warunki są spełnione, generuje zgłoszenie o wysokim stopniu istotności w zakresie zarządzania.
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 identyfikuje, KTÓRA zmienna ma pierwszeństwo — nigdy jej wartość
}, true
}
Szczegółowy skrót znalezienia rejestruje która zmienna statyczna jest obecna i który sygnał federacji zasłania, nigdy nie osadzając wartości klucza. Nawet w formie maskowanej. Skrót jest stabilny między uruchomieniami, więc silnik zarządzający go deduplikuje, a SIEM może go wyszukiwać według sprawy.
Statyczny klucz bez używanej federacji jest po prostu statycznym kluczem — łącznik go nie oznacza. Znalezienie aktywuje się tylko wtedy, gdy obu istnieją, ponieważ to jest konkretna konfiguracja, w której operator uważa, że federacja jest aktywna, podczas gdy tak nie jest.
Pętla uzgadniania: zadeklarowane vs. faktyczne
Wykrycie problemu z kluczem statycznym to połowa obrazu. Druga połowa to weryfikacja, czy sama konfiguracja federacji nie uległa dryfowi. Connector utrzymuje zadeklarowaną bazową konfigurację — zasady federacji, które operator wyraźnie deklaruje jako obowiązujące — i porównuje ją ze stanem rzeczywistym konfiguracji WIF organizacji Anthropic.
Stan rzeczywisty pochodzi z trzech punktów końcowych WIF Admin API:
GET /v1/organizations/service_accounts— konta usług (svac_), do których odnoszą się zasady federacjiGET /v1/organizations/federation_issuers— wystawcy OIDC/SPIFFE (fdis_), którym organizacja ufaGET /v1/organizations/federation_rules— zasady (fdrl_), które wiążą wystawców z kontami usług
Te punkty końcowe wymagają tokena nosiciela org:admin OAuth — odrębnego poświadczenia od klucza Admin API sk-ant-admin, który jest używany przez listę uczestników. Admin API WIF jawnie odrzuca klucze Admin API, dlatego łącznik używa osobnego uwierzytelnionego klienta do rekonsyliacji.
Różnica między zadeklarowanym a rzeczywistym tworzy siedem kategorii wyników:
| Przypadek odchylenia | Co to oznacza | Waga |
|---|---|---|
undeclared_live_rule | Rzeczywista zasada, której operator nigdy nie zadeklarował lub nie nadzoruje | Wysoka |
declared_rule_not_live | Zadeklarowana zasada, która już nie istnieje w górze | Średnia |
scope_drift | Aktywny zakres różni się od zadeklarowanego zakresu | Średni (Wysoki, jeśli rozszerzony na całą organizację) |
lifetime_drift | Czas życia aktywnego tokena różni się od zadeklarowanego | Średni (Wysoki, jeśli dłuższy niż zadeklarowany) |
over_broad_subject | Aktywna reguła bez rzeczywistego ograniczenia podmiotu | Średni |
orphan_rule | Reguła odwołująca się do brakującego wydawcy lub konta serwisowego | Średni |
orphan_issuer | Wydawca, do którego nie odnosi się żadna reguła | Niski |
Dwie sprawy eskalują do wysokiej wagi automatycznie: aktywny zakres, który rozszerzył się do całej organizacji lub zakresu administracyjnego, którego operator nie zadeklarował, oraz okres życia aktywnego tokena, który jest dłuższy niż ustanowiony standard. Oba zwiększają promień wpływu poza to, na co zgodził się operator. Normalizacja zakresu (przycinanie, usuwanie duplikatów, sortowanie) zapobiega fałszywym trafieniom wynikającym z różnic w białych znakach lub kolejności.
Kiedy żaden token org:admin nie jest skonfigurowany, proces uzgadniania po prostu się nie uruchamia. Connector działa tylko z deklarowanym standardem bazowym i jest uczciwy co do swojego pokrycia: nigdy nie tworzy fałszywego aktywnego wykazu. Gdy nie można uzyskać dostępu do aktywnego API (błąd sieci, wygaśnięcie tokena), generuje pojedyncze znalezienie reconciliation_unavailable i kontynuuje – uprawnienia wykazu i wykrywanie zagrożeń nie powinny być powiązane ze stanem tokena org:admin.
Czytaj-pierwsze i minimalne dane
Konektor przestrzega ścisłej umowy dotyczącej minimalizacji danych. Każde wywołanie API jest typu GET. Nigdy nie tworzy, nie aktualizuje ani nie usuwa obiektu Anthropic. Przenosi wyłącznie metadane tożsamości: identyfikatory, imiona, adresy e-mail, role, wskazówki dotyczące kluczy. Nigdy sekretu klucza. Nigdy klucza prywatnego. Nigdy wybitego tokena w stanie spoczynku.
Wybijany token z wymiany WIF jest zwracany wywołującemu i nigdy nie jest logowany, przechowywany ani emitowany przez konektor. Jedynym zapisem, który trafia do rejestru zarządzania, jest struktura ExchangeAudit — która celowo nie zawiera żadnego tokena:
type ExchangeAudit struct {
FederationRuleID string
OrganizationID string
ServiceAccountID string
WorkspaceID string
Scope string
TokenType string
ExpiresAt time.Time
}
Ta sama minimalizacja dotyczy synchronizacji na żywo. Gdy konektor odczytuje konfigurację JWKS wydawcy federacji, redukuje odpowiedź do dwóch wartości logicznych: trybu odkrywania (discovery, explicit_url lub inline) oraz czy jest przypięty niestandardowy certyfikat CA. Materiał JWK w linii — klucze publiczne, ale obszerne i nigdy niepotrzebne do decyzji rządzenia — nigdy nie jest dekodowany do przechowywanego lub emitowanego pola. Certyfikat CA w formacie PEM jest dekodowany tylko po to, aby uzyskać flagę obecności i jest natychmiast odrzucany.
Warunki CEL dotyczące zasad federacji są uwzględniane w analizie postawy (wyrażenie CEL reguły jest częścią jej granicy bezpieczeństwa), ale nigdy nie są oceniane. Konektor nie ma zależności od silnika CEL — ocena to osobna kwestia.
Co to umożliwia
Statyczne klucze mieszają uwierzytelnianie z tożsamością. Każda sesja jest tym samym podmiotem, każdy token żyje wiecznie, a obecność klucza w pliku konfiguracyjnym cicho wyłącza jakąkolwiek federację, która miała cię chronić.
WIF rozdziela te dwa elementy. Uwierzytelnianie odbywa się poprzez poświadczone stwierdzenie, które udowadnia, że obciążenie robocze jest tym, za kogo się podaje. Tożsamość jest ograniczona do sesji: token o krótkim czasie życia, określone konto usługi, konkretna przestrzeń robocza, zadeklarowany zakres. Gdy token wygaśnie, obciążenie robocze ponownie się poświadcza. Gdy konfiguracja odbiega od normy, pętla uzgadniania ujawnia gap. Gdy statyczny klucz zasłania cały mechanizm, łącznik wykrywa go i raportuje, zanim stanie się to zagadnieniem incydentowym.
Wynikiem jest model tożsamości, w którym poświadczenia wygasają, sesje są przypisywalne, a założenia dotyczące zarządzania są ciągle weryfikowane w stosunku do rzeczywistego stanu organizacji — a nie tylko stanu, jaki ktoś przewidywał.
Connector tożsamości, wymiana WIF oraz wykrywanie błędnych działań są częścią modułu zarządzania tożsamościami. Dla szerszego modelu bezpieczeństwa — rejestru, mapy dostępu, architektury kolekcji read-first — zobacz bezpieczeństwo.