Vai al contenuto

WIF

Credenziali temporanee per Claude Code con federazione delle identità del carico di lavoro

Scritto da Olivares AI 9 min di lettura

Distribuisci Claude Code per un team della piattaforma. Configura la federazione delle identità del carico di lavoro di Anthropic in modo che ogni sessione scambi un’asserzione OIDC attestata con un token di breve durata. La verifica della sicurezza è pulita: nessuna chiave statica, identità per sessione, token che scadono. La mattina successiva, un ingegnere aggiunge export ANTHROPIC_API_KEY=sk-ant-... al profilo della shell perché è necessario per uno script. La federazione viene ora ignorata silenziosamente per ogni sessione eseguita dal tecnico. Nessun errore, nessun avviso, nessuna voce di registro. La chiave statica ha la precedenza e il percorso attestato non viene mai richiamato.

Questa non è una condizione di gara teorica. Si tratta dell’ordine di risoluzione delle credenziali documentato di Anthropic ed è il modo più comune in cui le distribuzioni della federazione falliscono silenziosamente.

Il problema con le chiavi statiche

Il modo predefinito per autenticare Claude Code è una chiave API statica: una stringa sk-ant- impostata come ANTHROPIC_API_KEY. Funziona e presenta tre proprietà che sono in conflitto con la gestione dell’identità aziendale.

Nessuna scadenza, nessun segnale di rotazione. Una chiave statica è valida finché qualcuno non la revoca. Non esiste una durata integrata, nessun promemoria di rotazione, nessun meccanismo che imponga la riautenticazione. Una chiave emessa durante un proof-of-concept può comunque autenticare i carichi di lavoro di produzione anche mesi dopo.

Identità condivisa. Ogni sessione che utilizza la stessa chiave esegue l’autenticazione con la stessa entità. L’audit trail mostra quale area di lavoro è stata utilizzata, ma non è in grado di distinguere quale tecnico, quale macchina o quale automazione ha eseguito una determinata richiesta. L’attribuzione per sessione è strutturalmente impossibile.

Precedenza silenziosa sulla federazione. Questa è la pistola. La risoluzione delle credenziali di Anthropic posiziona la chiave statica (ANTHROPIC_API_KEY, livello 2) sopra il percorso della federazione (livello 4). Quando entrambi esistono nello stesso ambiente, prevale la chiave statica. Lo scambio federativo non viene mai tentato. Non viene sollevato alcun errore. Il carico di lavoro viene eseguito correttamente con l’identità della chiave statica e ogni presupposto di governance basato sulla federazione (identità con ambito sessione, asserzioni attestate, scadenza dei token) viene automaticamente invalidato.

Anche una variabile vuota (ANTHROPIC_API_KEY="") occupa il suo slot di precedenza. L’autenticazione fallirà, ma impedirà comunque al runtime di raggiungere il percorso della federazione. La modalità di errore è “errore di autenticazione”, non “caduto nella federazione”.

Come funziona la federazione delle identità del carico di lavoro

WIF sostituisce la chiave statica con uno scambio: entra un’asserzione verificata, esce un token di breve durata. L’asserzione è un JWT: un JWT-SVID di un provider di identità SPIFFE o un token OIDC standard di qualsiasi emittente considerato attendibile dall’organizzazione Anthropic.

Lo scambio segue RFC 7523 (JWT borsa al portatore). Il carico di lavoro presenta la sua asserzione all’endpoint token di Anthropic insieme a tre identificatori: a quale regola di federazione corrispondere (fdrl_), quale account di servizio fungere (svac_) e a quale organizzazione appartiene lo scambio. Nel codice, il nucleo dello scambio è un POST con un corpo JSON:

type exchangeRequest struct {
    GrantType        string `json:"grant_type"`         // "urn:ietf:params:oauth:grant-type:jwt-bearer"
    Assertion        string `json:"assertion"`          // il JWT-SVID o token OIDC verificato
    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"`
}

La risposta è una risposta token RFC 6749. Il token coniato porta il prefisso sk-ant-oat (OAT = OAuth Access Token), un ambito dichiarato (workspace:developer per l’accesso API non amministrativo completo o org:manage_tunnels per la gestione del tunnel MCP) e una durata compresa tra 60 secondi e 24 ore.

Non esiste alcun token di aggiornamento. Quando il token coniato scade, il carico di lavoro deve ripresentare la sua asserzione ed eseguire nuovamente lo scambio. Questo è intenzionale: l’asserzione stessa è attestata (verificata a monte dall’API del carico di lavoro SPIFFE o dal provider OIDC), quindi ogni nuovo scambio è una nuova attestazione. Un token compromesso è utile solo per la sua durata rimanente e non esiste alcun percorso di aggiornamento che un utente malintenzionato possa sfruttare per estenderlo.

Il risultato è l’identità per sessione. Ciascuna sessione Claude Code scambia la propria asserzione, riceve il proprio token di breve durata e si autentica come principale distinto. L’audit trail registra quale account di servizio ha agito, legato a quale regola di federazione e con quale ambito di lavoro.

Rilevamento della pistola della federazione con tasti e ombre statiche

La distribuzione di WIF non è sufficiente. È inoltre necessario rilevare quando qualcosa nell’ambiente lo aggira silenziosamente. Il connettore controlla l’ambiente di runtime per la presenza di una credenziale statica (ANTHROPIC_API_KEY o ANTHROPIC_AUTH_TOKEN) e controlla se la federazione è contemporaneamente in uso (tramite le regole di federazione dichiarate o attraverso il segnale ANTHROPIC_IDENTITY_TOKEN_FILE). Quando entrambe le condizioni sono vere, viene generato un risultato di governance con gravità elevata.

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 identifica QUALE variabile ha la precedenza — mai il valore
    }, true
}

L’hash di dettaglio del risultato registra quale variabile statica è presente e quale federazione la segnala, senza mai incorporare il valore della chiave. Nemmeno una forma mascherata. L’hash è stabile tra le esecuzioni, quindi il motore di governance lo deduplica e un SIEM può interrogarlo in base al caso.

Una chiave statica senza federazione in uso è semplicemente una chiave statica: il connettore non la contrassegna. La ricerca viene attivata solo quando esistono entrambe, perché questa è la configurazione specifica in cui l’operatore ritiene che la federazione sia attiva e non lo è.

Il ciclo di riconciliazione: dichiarato vs. effettivo

Rilevare la pistola a chiave statica è metà dell’opera. L’altra metà sta verificando che la configurazione stessa della federazione non abbia subito deviazioni. Il connettore mantiene una linea di base dichiarata (le regole della federazione che l’operatore dichiara esplicitamente come governate) e le confronta con lo stato attivo della configurazione WIF dell’organizzazione Anthropic.

Lo stato live proviene da tre endpoint API di amministrazione WIF:

  • GET /v1/organizations/service_accounts: gli account di servizio (svac_) a cui sono destinate le regole della federazione
  • GET /v1/organizations/federation_issuers: gli emittenti OIDC/SPIFFE (fdis_) di cui l’organizzazione si fida
  • GET /v1/organizations/federation_rules — le regole (fdrl_) che vincolano gli emittenti ai conti di servizio

Questi endpoint richiedono un token bearer org:admin OAuth: una credenziale distinta dalla chiave API di amministrazione sk-ant-admin utilizzata dal roster. L’API di amministrazione WIF rifiuta esplicitamente le chiavi API di amministrazione, motivo per cui il connettore utilizza un client autenticato separato per la riconciliazione.

La differenza tra dichiarato e vivo produce sette categorie di risultati:

Caso di derivaCosa significaGravità
undeclared_live_ruleUna regola viva che l’operatore non ha mai dichiarato né governaAlto
declared_rule_not_liveUna regola dichiarata che non esiste più a monteMedio
scope_driftL’ambito live differiva dall’ambito dichiaratoMedio (alto se esteso a tutta l’organizzazione)
lifetime_driftLa durata del token live è diversa da quella dichiarataMedio (Alto se più lungo di quanto dichiarato)
over_broad_subjectUna regola viva senza alcun reale vincolo di soggettoMedio
orphan_ruleUna regola che fa riferimento a un emittente o a un account di servizio mancanteMedio
orphan_issuerUn emittente a cui non fa riferimento alcuna regolaBasso

Due casi passano automaticamente a un livello di gravità elevato: un ambito attivo che si è esteso a un ambito a livello di organizzazione o amministrativo non dichiarato dall’operatore e una durata del token attivo che è più lunga della base di riferimento governata. Entrambi ampliano il raggio dell’esplosione oltre ciò che l’operatore ha autorizzato. La normalizzazione dell’ambito (rifinitura, deduplicazione, ordinamento) impedisce i falsi positivi derivanti da spazi bianchi o differenze di ordinamento.

Quando non è configurato alcun token org:admin, il passaggio di riconciliazione semplicemente non viene eseguito. Il connettore funziona solo con la linea di base dichiarata ed è onesto riguardo alla sua copertura: non crea mai un elenco dal vivo. Quando non è possibile raggiungere l’API live (errore di rete, scadenza del token), emette un singolo risultato reconciliation_unavailable e continua: le concessioni del roster e il rilevamento del footgun non devono essere accoppiati alla salute del token org:admin.

Dati di prima lettura e minimi

Il connettore segue un rigoroso contratto di minimizzazione dei dati. Ogni chiamata API è un GET. Non crea, aggiorna o elimina mai un oggetto Anthropic. Contiene solo metadati di identità: ID, nomi, e-mail, ruoli, suggerimenti chiave. Mai un segreto chiave. Mai una chiave privata. Mai un gettone coniato a riposo.

Il token coniato dallo scambio WIF viene restituito al chiamante e non viene mai registrato, persistente o emesso dal connettore. L’unico record che raggiunge il registro della governance è la struttura ExchangeAudit, che deliberatamente non contiene alcun token:

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

La stessa minimizzazione si applica alla riconciliazione live. Quando il connettore legge la configurazione JWKS di un emittente della federazione, riduce la risposta a due valori booleani: la modalità di rilevamento (discovery, explicit_url o inline) e se è bloccato un certificato CA personalizzato. Il materiale JWK in linea - chiavi pubbliche, ma ingombranti e mai necessarie per decisioni di governance - non viene mai decodificato in un campo memorizzato o emesso. Il certificato CA PEM viene decodificato solo per ricavare un flag di presenza e viene immediatamente scartato.

Le condizioni CEL sulle regole di federazione vengono effettuate per l’analisi della situazione (l’espressione CEL di una regola fa parte del suo limite di sicurezza), ma non vengono mai valutate. Il connettore non ha alcuna dipendenza dal motore CEL: la valutazione è una questione separata.

Ciò che ciò consente

Le chiavi statiche confondono l’autenticazione con l’identità. Ogni sessione ha lo stesso principio, ogni token vive per sempre e la presenza di una chiave in un dotfile disabilita silenziosamente qualsiasi federazione che pensavi ti proteggesse.

WIF separa i due. L’autenticazione avviene tramite un’asserzione attestata che dimostra che il carico di lavoro è colui che sostiene. L’identità ha come ambito la sessione: un token di breve durata, un account di servizio specifico, un’area di lavoro specifica, un ambito dichiarato. Quando il token scade, il carico di lavoro viene nuovamente attestato. Quando la configurazione va alla deriva, il ciclo di riconciliazione emerge su gap. Quando una chiave statica oscura l’intero meccanismo, il connettore la rileva e la segnala prima che diventi una rilevazione di incidente.

Il risultato è un modello di identità in cui le credenziali scadono, le sessioni sono attribuibili e le ipotesi di governance vengono continuamente verificate rispetto allo stato effettivo dell’organizzazione, non solo allo stato previsto da qualcuno.


Il connettore di identità, lo scambio WIF e il rilevamento delle armi da fuoco fanno parte del modulo di gestione dell’identità. Per il modello di sicurezza più ampio (il registro, la mappa di accesso, l’architettura di raccolta read-first) vedere sicurezza.

Articoli correlati

Domande frequenti

La chiave statica deve contenere una chiave API valida per la federazione shadow?

No. La precedenza delle credenziali di Anthropic si basa sulla presenza, non sulla validità. Un ANTHROPIC_API_KEY="" vuoto ottiene il suo slot di precedenza proprio come farebbe una chiave popolata: si trova sopra i livelli della federazione nell'ordine di risoluzione, quindi il runtime non rientra mai nel percorso attestato. Il connettore lo rileva controllando se la variabile di ambiente è impostata (os.LookupEnv), non se il suo valore non è vuoto. Una chiave vuota fallirà l'autenticazione, ma oscurerà la federazione tanto quanto una chiave reale.

Cosa succede se non ho un token org:admin OAuth per la riconciliazione in tempo reale?

Il connettore funziona senza di esso. Modella esattamente ciò che l'operatore dichiara nella configurazione della federazione, emette il registro NHI governato e i margini di concessione consentiti da tali dichiarazioni ed esegue il rilevamento del fucile a chiave statica. Salta semplicemente il passaggio di riconciliazione in tempo reale, perché gli endpoint dell'API di amministrazione WIF rifiutano qualsiasi cosa diversa da un token bearer org:admin OAuth. Ottieni una linea di base solo dichiarata che è onesta riguardo alla sua copertura: non crea mai un elenco dal vivo.

Scopri cosa possono raggiungere i tuoi agenti

Olivares AI è la piattaforma aperta e in self-hosting per il tuo parco AI. Distribuiscila sulla tua infrastruttura e ottieni la mappa degli accessi che i tuoi team security e platform richiedono.