Je implementeert Claude Code voor een platformteam. U configureert de werkbelastingidentiteitsfederatie van Anthropic zodat elke sessie een bevestigde OIDC-definitie uitwisselt voor een token met een korte levensduur. De beveiligingsbeoordeling is schoon: geen statische sleutels, identiteit per sessie, tokens die verlopen. De volgende ochtend voegt een ingenieur export ANTHROPIC_API_KEY=sk-ant-... toe aan zijn shellprofiel omdat een script dit nodig heeft. Federatie wordt nu stilzwijgend omzeild voor elke sessie die de engineer uitvoert. Geen fout, geen waarschuwing, geen loginvoer. De statische sleutel heeft voorrang en het geattesteerde pad wordt nooit aangeroepen.
Dit is geen theoretische raceconditie. Het is de gedocumenteerde oplossingsvolgorde van Anthropic, en het is de meest voorkomende manier waarop federatie-implementaties stilletjes mislukken.
Het probleem met statische sleutels
De standaardmanier om Claude Code te verifiëren is een statische API-sleutel: een sk-ant--tekenreeks die is ingesteld als ANTHROPIC_API_KEY. Het werkt en heeft drie eigenschappen die in strijd zijn met bedrijfsidentiteitsbeheer.
Geen vervaldatum, geen rotatiesignaal. Een statische sleutel is geldig totdat iemand deze intrekt. Er is geen ingebouwde levensduur, geen rotatieherinnering, geen mechanisme dat herauthenticatie afdwingt. Een sleutel die tijdens een proof-of-concept wordt uitgegeven, kan maanden later nog steeds de productieworkloads verifiëren.
Gedeelde identiteit. Elke sessie die dezelfde sleutel gebruikt, authenticeert als dezelfde opdrachtgever. Het audittraject laat zien welke werkruimte er is gebruikt, maar kan niet onderscheiden welke ingenieur, welke machine of welke automatisering een bepaald verzoek heeft uitgevoerd. Attributie per sessie is structureel onmogelijk.
Stille voorrang boven federatie. Dit is het voetgeweer. De inloggegevensresolutie van Anthropic plaatst de statische sleutel (ANTHROPIC_API_KEY, laag 2) boven het federatiepad (laag 4). Wanneer beide in dezelfde omgeving bestaan, wint de statische sleutel. De federatie-uitwisseling wordt nooit geprobeerd. Er is geen fout opgetreden. De werklast wordt met succes uitgevoerd onder de identiteit van de statische sleutel, en elke governance-aanname die is gebaseerd op federatie (sessiegerichte identiteit, bevestigde beweringen, tokenverval) wordt stilzwijgend ongeldig verklaard.
Zelfs een lege variabele (ANTHROPIC_API_KEY="") neemt zijn voorrangspositie in beslag. De authenticatie mislukt, maar het voorkomt nog steeds dat de runtime het federatiepad bereikt. De foutmodus is “authenticatiefout”, niet “doorvallen naar federatie”.
Hoe werkbelastingidentiteitsfederatie werkt
WIF vervangt de statische sleutel door een uitwisseling: een geverifieerde bewering gaat erin, een kortstondig token komt eruit. De bewering is een JWT - ofwel een JWT-SVID van een SPIFFE-identiteitsprovider, of een standaard OIDC-token van een uitgever die de Anthropic-organisatie vertrouwt.
De uitwisseling volgt op RFC 7523 (JWT toondersubsidie). De werklast presenteert zijn bewering aan het tokeneindpunt van Anthropic, samen met drie identificatiegegevens: welke federatieregel moet worden vergeleken (fdrl_), welk serviceaccount moet fungeren als (svac_) en tot welke organisatie de uitwisseling behoort. In code is de kern van de uitwisseling een POST met een JSON-body:
type exchangeRequest struct {
GrantType string `json:"grant_type"` // "urn:ietf:params:oauth:grant-type:jwt-bearer"
Assertion string `json:"assertion"` // de geverifieerde JWT-SVID of het OIDC-token
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"`
}
Het antwoord is een tokenantwoord RFC 6749. Het geslagen token heeft het voorvoegsel sk-ant-oat (OAT = OAuth Access Token), een gedeclareerde scope (workspace:developer voor volledige niet-administratieve API-toegang, of org:manage_tunnels voor MCP-tunnelbeheer) en een levensduur tussen 60 seconden en 24 uur.
Er is geen vernieuwingstoken. Wanneer het geslagen token verloopt, moet de werklast zijn bewering opnieuw presenteren en de uitwisseling opnieuw uitvoeren. Dit is opzettelijk: de bewering zelf wordt bevestigd (stroomopwaarts geverifieerd door de SPIFFE-workload-API of de OIDC-provider), dus elke nieuwe uitwisseling is een herbevestiging. Een gecompromitteerd token is alleen nuttig gedurende de resterende levensduur ervan, en er is geen vernieuwingspad dat een aanvaller kan misbruiken om het te verlengen.
Het resultaat is identiteit per sessie. Elke Claude Code-sessie wisselt zijn eigen bewering uit, ontvangt zijn eigen kortstondige token en authenticeert als een afzonderlijke opdrachtgever. In het audittraject wordt vastgelegd welk serviceaccount actie heeft ondernomen, gebonden is aan welke federatieregel en binnen welke werkruimte valt.
Het detecteren van het voetgeweer van de statische sleutel-schaduwfederatie
Het implementeren van WIF is niet voldoende. Je moet ook detecteren wanneer iets in de omgeving dit stilletjes omzeilt. De connector inspecteert de runtime-omgeving op de aanwezigheid van een statische referentie (ANTHROPIC_API_KEY of ANTHROPIC_AUTH_TOKEN) en controleert of federatie tegelijkertijd in gebruik is (via gedeclareerde federatieregels of via het ANTHROPIC_IDENTITY_TOKEN_FILE-signaal). Wanneer beide voorwaarden waar zijn, geeft dit een zeer ernstige governance-bevinding weer.
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 identificeert WELKE variabele voorrang krijgt — nooit de waarde
}, true
}
De detailhash van de bevinding registreert welke statische variabele aanwezig is en welke federatie signaleert dat deze in de schaduw staat, zonder ooit de waarde van de sleutel in te sluiten. Zelfs geen gemaskerde vorm. De hash is stabiel over de uitvoeringen heen, dus de governance-engine ontdubbelt deze en een SIEM kan er per geval query’s op uitvoeren.
Een statische sleutel waarvoor geen federatie in gebruik is, is slechts een statische sleutel; de connector markeert deze niet. De bevinding wordt alleen geactiveerd als beide bestaan, omdat dat de specifieke configuratie is waarbij de operator denkt dat federatie actief is en dat is niet het geval.
De verzoeningslus: verklaard versus feitelijk
Het detecteren van het statische sleutelvoetgeweer is de helft van het plaatje. De andere helft verifieert dat de federatieconfiguratie zelf niet is afgedreven. De connector onderhoudt een gedeclareerde basislijn (de federatieregels die de operator expliciet als beheerd declareert) en vergelijkt deze met de livestatus van de WIF-configuratie van de Anthropic-organisatie.
De livestatus is afkomstig van drie WIF Admin API-eindpunten:
GET /v1/organizations/service_accounts: de serviceaccounts (svac_) waarop federatieregels zijn gerichtGET /v1/organizations/federation_issuers- de OIDC/SPIFFE-uitgevende instellingen (fdis_) die de organisatie vertrouwtGET /v1/organizations/federation_rules- de regels (fdrl_) die uitgevers aan serviceaccounts binden
Voor deze eindpunten is een org:admin OAuth-bearertoken vereist: een andere referentie dan de sk-ant-admin Admin API-sleutel die door het rooster wordt gelezen. De WIF Admin API weigert expliciet Admin API-sleutels. Daarom gebruikt de connector een aparte, geverifieerde client voor afstemming.
Het verschil tussen aangegeven en live levert zeven categorieën van bevindingen op:
| Drift geval | Wat het betekent | Ernst |
|---|---|---|
undeclared_live_rule | Een live regel die de operator nooit heeft verklaard of beheerst | Hoog |
declared_rule_not_live | Een verklaarde regel die stroomopwaarts niet meer bestaat | Middelmatig |
scope_drift | De live scope week af van de aangegeven scope | Gemiddeld (Hoog indien verbreed naar de hele organisatie) |
lifetime_drift | De live-tokenlevensduur week af van de aangegeven levensduur | Medium (Hoog indien langer dan aangegeven) |
over_broad_subject | Een live regel zonder echte onderwerpbeperking | Middelmatig |
orphan_rule | Een regel die verwijst naar een ontbrekende uitgever of serviceaccount | Middelmatig |
orphan_issuer | Een uitgevende instelling waarnaar door geen enkele regel wordt verwezen | Laag |
Twee gevallen escaleren automatisch naar een hoge ernst: een live bereik dat zich heeft uitgebreid tot een organisatiebreed of beheerdersbereik dat de operator niet heeft aangegeven, en een live tokenlevensduur die langer is dan de beheerde basislijn. Beide vergroten de explosieradius verder dan wat de operator heeft ondertekend. Scope-normalisatie (bijsnijden, ontdubbelen, sorteren) voorkomt valse positieven door witruimte of ordeningsverschillen.
Als er geen org:admin-token is geconfigureerd, wordt de afstemmingspas eenvoudigweg niet uitgevoerd. De connector werkt met de alleen-gedeclareerde basislijn en is eerlijk over de dekking ervan: er wordt nooit een live rooster gemaakt. Wanneer de live API niet kan worden bereikt (netwerkfout, verlopen token), zendt deze een enkele reconciliation_unavailable-bevinding uit en gaat verder: de roostertoekenningen en de footgun-detectie mogen niet worden gekoppeld aan de gezondheid van het org:admin-token.
Eerst lezen en minimale gegevens
De connector volgt een strikt dataminimalisatiecontract. Elke API-aanroep is een GET. Er wordt nooit een Anthropic-object gemaakt, bijgewerkt of verwijderd. Het bevat alleen identiteitsmetagegevens: ID’s, namen, e-mails, rollen, belangrijke hints. Nooit een sleutelgeheim. Nooit een privésleutel. Nooit een geslagen teken in rust.
Het aangemaakte token van de WIF-centrale wordt teruggestuurd naar de beller en wordt nooit geregistreerd, bewaard of verzonden door de connector. Het enige record dat het governance-grootboek bereikt, is de ExchangeAudit-structuur - die opzettelijk geen token bevat:
type ExchangeAudit struct {
FederationRuleID string
OrganizationID string
ServiceAccountID string
WorkspaceID string
Scope string
TokenType string
ExpiresAt time.Time
}
Dezelfde minimalisering geldt voor de live verzoening. Wanneer de connector de JWKS-configuratie van een federatie-uitgever leest, wordt de reactie beperkt tot twee booleans: de detectiemodus (discovery, explicit_url of inline) en of een aangepast CA-certificaat is vastgemaakt. Het inline JWK-materiaal – publieke sleutels, maar omvangrijk en nooit nodig voor bestuursbeslissingen – wordt nooit gedecodeerd in een opgeslagen of uitgezonden veld. Het CA-certificaat PEM wordt alleen gedecodeerd om een aanwezigheidsvlag af te leiden en wordt onmiddellijk weggegooid.
CEL-voorwaarden voor federatieregels worden meegenomen voor postuuranalyse (de CEL-expressie van een regel maakt deel uit van de beveiligingsgrens), maar ze worden nooit geëvalueerd. De connector is niet afhankelijk van de CEL-engine; evaluatie is een apart aandachtspunt.
Wat dit mogelijk maakt
Statische sleutels combineren authenticatie met identiteit. Elke sessie is hetzelfde principe, elk token leeft voor altijd, en de aanwezigheid van een sleutel in een dotfile schakelt stilletjes elke federatie uit waarvan je dacht dat die je zou beschermen.
WIF scheidt de twee. Authenticatie vindt plaats via een bevestigde bewering die bewijst dat de werklast is wie deze claimt. Identiteit is afgestemd op de sessie: een token met een korte levensduur, een specifiek serviceaccount, een specifieke werkruimte, een aangegeven bereik. Wanneer het token verloopt, wordt de werkbelasting opnieuw bevestigd. Wanneer de configuratie afwijkt, komt de afstemmingslus naar de gap. Wanneer een statische sleutel het hele mechanisme overschaduwt, detecteert de connector dit en rapporteert dit voordat het een incidentbevinding wordt.
Het resultaat is een identiteitsmodel waarin inloggegevens verlopen, sessies kunnen worden toegeschreven en aannames over het bestuur voortdurend worden geverifieerd aan de hand van de werkelijke toestand van de organisatie – en niet alleen de toestand die iemand bedoelde.
De identiteitsconnector, de WIF-uitwisseling en de footgun-detectie maken deel uit van de identiteitsbeheermodule. Voor het bredere beveiligingsmodel (het grootboek, de toegangskaart, de read-first collectiearchitectuur) zie security.