Saltar para o conteúdo

WIF

Credenciais efêmeras para Claude Code com federação de identidade de carga de trabalho

Por Olivares AI 10 min de leitura

Você implanta Claude Code para uma equipe de plataforma. Você configura a federação de identidade de carga de trabalho do Anthropic de modo que cada sessão troque uma asserção OIDC atestada por um token de curta duração. A revisão de segurança está limpa: sem chaves estáticas, identidade por sessão, tokens que expiram. Na manhã seguinte, um engenheiro adiciona export ANTHROPIC_API_KEY=sk-ant-... ao perfil do seu shell porque um script precisa dele. A federação agora é silenciosamente ignorada para cada sessão que esse engenheiro executa. Sem erro, sem aviso, sem registro. A chave estática tem precedência e o caminho atestado nunca é invocado.

Isso não é uma condição de corrida teórica. É a ordem de resolução de credenciais documentada do Anthropic e é a maneira mais comum de as implantações de federação falharem silenciosamente.

O problema com chaves estáticas

A forma padrão de autenticar Claude Code é uma chave de API estática: uma string sk-ant- definida como ANTHROPIC_API_KEY. Funciona, e possui três propriedades que entram em conflito com o gerenciamento de identidade empresarial.

Sem expiração, sem sinal de rotação. Uma chave estática é válida até que alguém a revogue. Não há tempo de vida incorporado, nenhum lembrete de rotação, nenhum mecanismo que force a reautenticação. Uma chave emitida durante uma prova de conceito ainda pode autenticar cargas de trabalho de produção meses depois.

Identidade compartilhada. Cada sessão usando a mesma chave autentica como o mesmo principal. O registro de auditoria mostra qual espaço de trabalho foi usado, mas não pode distinguir qual engenheiro, qual máquina ou qual automação executou uma determinada solicitação. A atribuição por sessão é estruturalmente impossível.

Precedência silenciosa sobre a federação. Este é o problema crítico. A resolução de credenciais do Anthropic coloca a chave estática (ANTHROPIC_API_KEY, nível 2) acima do caminho de federação (nível 4). Quando ambos existem no mesmo ambiente, a chave estática prevalece. A troca de federação nunca é tentada. Nenhum erro é gerado. A carga de trabalho é executada com sucesso sob a identidade da chave estática, e todas as suposições de governança baseadas na federação – identidade com escopo de sessão, declarações atestadas, expiração de token – são silenciosamente invalidadas.

Mesmo uma variável vazia (ANTHROPIC_API_KEY="") ocupa seu slot de precedência. Ela falhará na autenticação, mas ainda assim impedirá que o tempo de execução alcance o caminho de federação. O modo de falha é “erro de autenticação”, não “passar pelo caminho de federação”.

Como funciona a federação de identidade da carga de trabalho

WIF substitui a chave estática por uma troca: uma asserção verificada entra, um token de curta duração sai. A asserção é um JWT — seja um JWT-SVID de um provedor de identidade SPIFFE, ou um token OIDC padrão de qualquer emissor que a organização Anthropic confie.

A troca segue RFC 7523 (concessão de portador JWT). A carga de trabalho apresenta sua asserção ao endpoint de token do Anthropic junto com três identificadores: qual regra de federação aplicar (fdrl_), qual conta de serviço representar (svac_) e a qual organização a troca pertence. Em código, o núcleo da troca é um POST com um corpo JSON:

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

A resposta é uma resposta de token RFC 6749. O token cunhado contém o prefixo sk-ant-oat (OAT = OAuth Token de Acesso), um escopo declarado (workspace:developer para acesso completo à API não administrativa, ou org:manage_tunnels para gerenciamento de túnel MCP), e uma duração entre 60 segundos e 24 horas.

Não há token de atualização. Quando o token cunhado expira, a carga de trabalho deve reapresentar sua asserção e executar a troca novamente. Isso é deliberado: a própria asserção é atestada (verificada a montante pela API de carga de trabalho SPIFFE ou pelo provedor OIDC), portanto, cada nova troca é uma reatestação. Um token comprometido é útil apenas durante seu tempo de vida restante, e não há caminho de atualização que um invasor possa explorar para estendê-lo.

O resultado é a identidade por sessão. Cada sessão Claude Code troca sua própria asserção, recebe seu próprio token de curta duração e se autentica como um principal distinto. O registro de auditoria registra qual conta de serviço atuou, vinculada a qual regra de federação, com escopo em qual workspace.

Detectando a armadilha da federação de sombras de chave estática

Implantar WIF não é suficiente. Você também precisa detectar quando algo no ambiente o contorna silenciosamente. O conector inspeciona o ambiente de execução em busca da presença de uma credencial estática (ANTHROPIC_API_KEY ou ANTHROPIC_AUTH_TOKEN) e verifica se a federação está sendo usada simultaneamente (através de regras de federação declaradas ou pelo sinal ANTHROPIC_IDENTITY_TOKEN_FILE). Quando ambas as condições são verdadeiras, ele emite um achado de governança de alta gravidade.

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 QUAL variável tem precedência — nunca o valor
    }, true
}

O hash de detalhe da descoberta registra qual variável estática está presente e qual sinal de federação ela ofusca, sem nunca incluir o valor da chave. Nem mesmo uma forma mascarada. O hash é estável entre execuções, então o mecanismo de governança elimina duplicatas e um SIEM pode consultá-lo por caso.

Uma chave estática sem federação em uso é apenas uma chave estática — o conector não a sinaliza. A descoberta só é ativada quando ambos existem, porque essa é a configuração específica em que o operador acredita que a federação está ativa e não está.

O loop de reconciliação: declarado versus real

Detectar o risco de continuidade da chave estática é metade do processo. A outra metade é verificar se a própria configuração da federação não se desviou. O conector mantém uma linha de base declarada — as regras da federação que o operador declara explicitamente como governadas — e a compara com o estado atual da configuração da organização Anthropic WIF.

O estado atual vem de três endpoints da API Admin WIF:

  • GET /v1/organizations/service_accounts — as contas de serviço (svac_) que as regras da federação visam
  • GET /v1/organizations/federation_issuers — os emissores OIDC/SPIFFE (fdis_) em que a organização confia
  • GET /v1/organizations/federation_rules — as regras (fdrl_) que vinculam emissores a contas de serviço

Esses endpoints exigem um token bearer org:admin OAuth — uma credencial distinta da chave da API Admin sk-ant-admin que os leitores do roster usam. A API Admin WIF rejeita explicitamente chaves da API Admin, por isso o conector usa um cliente autenticado separado para reconciliação.

A diferença entre o declarado e o que está em execução produz sete categorias de achados:

Caso de derivaO que significaGravidade
undeclared_live_ruleUma regra em execução que o operador nunca declarou ou governaAlta
declared_rule_not_liveUma regra declarada que não existe mais a montanteMédia
scope_driftO escopo ativo divergiu do escopo declaradoMédio (Alto se ampliado para toda a organização)
lifetime_driftO tempo de vida do token ativo divergiu do declaradoMédio (Alto se mais longo que o declarado)
over_broad_subjectUma regra ativa sem restrição de sujeito realMédio
orphan_ruleUma regra referenciando um emissor ou conta de serviço ausenteMédio
orphan_issuerUm emissor referenciado por nenhuma regraBaixo

Dois casos escalonam para alta severidade automaticamente: um escopo ativo que se expandiu para um escopo em toda a organização ou escopo de administrador que o operador não declarou, e um tempo de vida de token ativo que é maior que o padrão governado. Ambos ampliam o raio de impacto além do que o operador autorizou. A normalização de escopo (reduzir, remover duplicatas, ordenar) previne falsos positivos devido a espaços em branco ou diferenças de ordenação.

Quando nenhum token org:admin está configurado, a passagem de reconciliação simplesmente não é executada. O conector trabalha apenas com o baseline declarado e é honesto sobre sua cobertura: ele nunca fabrica uma lista de ativos. Quando a API ativa não pode ser alcançada (erro de rede, expiração do token), ela emite apenas uma descoberta reconciliation_unavailable e continua — as permissões da lista e a detecção de armadilhas não devem estar vinculadas à saúde do token org:admin.

Leitura primeiro e dados mínimos

O conector segue um contrato rigoroso de minimização de dados. Cada chamada de API é um GET. Ele nunca cria, atualiza ou exclui um objeto Anthropic. Ele carrega apenas metadados de identidade: IDs, nomes, e-mails, funções, dicas de chaves. Nunca um segredo de chave. Nunca uma chave privada. Nunca um token cunhado em repouso.

O token cunhado a partir da troca WIF é retornado ao chamador e nunca é registrado, persistido ou emitido pelo conector. O único registro que chega ao livro de governança é a estrutura ExchangeAudit — que deliberadamente não carrega nenhum token:

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

A mesma minimização se aplica à reconciliação ao vivo. Quando o conector lê a configuração JWKS de um emissor de federação, ele reduz a resposta a dois valores booleanos: o modo de descoberta (discovery, explicit_url ou inline) e se um certificado CA personalizado está fixado. O material JWK embutido — chaves públicas, mas volumoso e nunca necessário para decisões de governança — nunca é decodificado em um campo armazenado ou emitido. O certificado CA em PEM é decodificado apenas para derivar um sinal de presença e é imediatamente descartado.

As condições CEL relacionadas às regras de federação são consideradas para análise de postura (a expressão CEL de uma regra faz parte do seu limite de segurança), mas nunca são avaliadas. O conector não possui dependência do mecanismo CEL — a avaliação é uma preocupação separada.

O que isso possibilita

Chaves estáticas confundem autenticação com identidade. Cada sessão é o mesmo principal, cada token vive para sempre, e a presença de uma chave em um arquivo oculto desativa silenciosamente qualquer federação que você pensava que estava te protegendo.

WIF separa os dois. A autenticação ocorre por meio de uma asserção atestada que prova que a carga de trabalho é quem afirma ser. A identidade é delimitada à sessão: um token de curta duração, uma conta de serviço específica, um espaço de trabalho específico, um escopo declarado. Quando o token expira, a carga de trabalho se reatestada. Quando a configuração se desvia, o ciclo de reconciliação evidencia o gap. Quando uma chave estática obscurece todo o mecanismo, o conector a detecta e a relata antes que se torne um achado de incidente.

O resultado é um modelo de identidade onde credenciais expiram, sessões são atribuíveis e as suposições de governança são continuamente verificadas em relação ao estado real da organização — não apenas ao estado que alguém pretendia.


O conector de identidade, a troca WIF e a detecção de armadilhas fazem parte do módulo de governança de identidade. Para o modelo de segurança mais amplo — o registro, o mapa de acesso, a arquitetura de coleta read-first — veja segurança.

Artigos relacionados

Perguntas frequentes

A chave estática precisa conter uma chave de API válida para substituir a federação?

Não. A precedência da credencial Anthropic é baseada na presença, não na validade. Uma ANTHROPIC_API_KEY="" vazia ganha seu slot de precedência da mesma forma que uma chave populada: ela fica acima dos níveis de federação na ordem de resolução, então o tempo de execução nunca cai no caminho atestado. O conector detecta isso verificando se a variável de ambiente está definida (os.LookupEnv), não se seu valor é não vazio. Uma chave vazia falhará na autenticação, mas ela ofuscará a federação tão completamente quanto uma real.

O que acontece se eu não tiver um token org:admin OAuth para reconciliação ao vivo?

O conector funciona sem ele. Ele modela exatamente o que o operador declara na configuração da federação, emite a lista NHI governada e as arestas de concessão permitidas a partir dessas declarações, e executa a detecção de problemas de chave estática. Ele simplesmente pula a passagem de reconciliação ao vivo, porque os endpoints da API Admin WIF rejeitam qualquer coisa que não seja um token portador org:admin OAuth. Você obtém uma linha de base apenas declarada que é honesta sobre sua cobertura: nunca fabrica uma lista ao vivo.

Veja a que os seus agentes conseguem aceder

A Olivares AI é a plataforma aberta e self-hosted para o seu ecossistema de IA. Implemente-a na sua própria infraestrutura e obtenha o mapa de acessos que as suas equipas de segurança e plataforma têm vindo a pedir.