mTLS para credenciais de máquina: quando um Bearer token não tem vínculo suficiente

Todo engenheiro de backend já emitiu um Bearer token ou uma API key para auth entre máquinas. Bem menos gente já emitiu mTLS. Os dois pertencem ao mesmo arco — como o servidor sabe quem está chamando — mas respondem perguntas diferentes, e o default só-Bearer-ou-chave é silenciosamente errado nas superfícies em que uma credencial vazada equivale a um takeover.
Esta é uma nota curta sobre o que mTLS de fato adiciona, quando ele compensa o peso, e o que o Authaz emite — tanto para credenciais M2M de OAuth quanto para API keys de admin.
Bearer token (ou API key) é um instrumento ao portador
O nome é o design. Quem segura a credencial é o chamador — ponto final. Não há vínculo com a conexão de rede, não há prova de posse, não há challenge na camada TLS. Um token ou chave capturado de uma linha de log, de um arquivo HAR vazado, de um proxy mal configurado, de um .env commitado por engano ou de uma SDK mal-comportada pode ser repetido de qualquer lugar, por qualquer coisa, até expirar (ou, no caso de API keys de longa duração, até alguém perceber).
POST /api/orders HTTP/1.1
Host: api.acme.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Content-Type: application/json
{ "sku": "SKU-001", "qty": 1 }O servidor checa:
1. signature(JWT, JWKS) ✓
2. exp / aud / iss ✓
3. aceita request como: subPOST /api/orders HTTP/1.1
Host: api.acme.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-Client-Cert: <leaf chain do handshake TLS>
{ "sku": "SKU-001", "qty": 1 }O servidor checa:
1. signature(JWT, JWKS) ✓
2. exp / aud / iss ✓
3. SHA256(client.cert) == cnf.x5t#S256 ✓
4. aceita request como: SPIFFE URI no SANO check de Bearer prova que o token está íntegro. O check de mTLS prova que o chamador possui a chave privada cujo certificado está vinculado a este token. Duas garantias diferentes.
O que mTLS de fato adiciona
Um certificado de cliente é apresentado durante o handshake TLS, antes do primeiro byte HTTP. O servidor recebe prova criptográfica de que o chamador possui a chave privada correspondente à chave pública do certificado — uma propriedade que nenhum token na camada de payload pode oferecer, porque o token é o payload.
Os benefícios empilham:
- A identidade está no certificado, não num header que o cliente afirma. Subject, SAN, SPIFFE URI — controlados pelo servidor na emissão.
- Sem segredo compartilhado na rede.
client_secreté uma senha; um handshake mTLS é challenge-response. - Vinculado à conexão. Um token vazado sozinho é inútil sem a chave privada correspondente. O comprometimento precisa atingir os dois.
- Revogável na trust anchor. Emita uma CA nova, aposente a antiga, todo certificado abaixo dela morre.
Quando um Bearer assinado é o suficiente
Para superfícies voltadas ao usuário — sessões de navegador, apps mobile, qualquer coisa em que um humano esteja no teclado — Bearer assinado sobre HTTPS é o default certo. O token é de curta duração, o usuário pode ser re-autenticado, e forçar um cert de cliente num fluxo de login é uma regressão de UX da qual as pessoas vão dar a volta. mTLS nessa camada está resolvendo o problema errado.
Quando mTLS compensa o peso
mTLS compensa o peso em superfícies de máquina, especialmente as com raio de explosão grande se uma credencial vaza:
- Serviço a serviço dentro de um tenant, onde um único pod comprometido não deveria conseguir chamar toda API interna se passando por qualquer um.
- Agentes de IA chamando APIs de terceiros — workloads de longa duração com credenciais sentadas em memória de processo e em traces de tool-use.
- Integrações B2B com parceiros, onde o token atravessa infra do parceiro, logs do parceiro e SDKs do parceiro antes de chegar no seu servidor.
- API keys de admin que mutam configuração — provisionar tenants, rotacionar credenciais, conceder papéis. Uma admin key vazada é um primitivo de supply chain; uma admin key vazada vinculada a um cert é muito menos interessante para quem encontrar.
- Workloads regulados (PCI, HIPAA-adjacente, financeiro), onde "a credencial vazou mas o cert não" é uma resposta significativamente diferente para um regulador do que "a credencial vazou".
- Webhooks de volta para o cliente, onde você quer que o cliente verifique que você chamou ele, não só que alguém com esse segredo chamou.
Se uma credencial roubada equivale a um takeover, você está em território de mTLS.
Access tokens vinculados a certificado (RFC 8705 §3)
Este é o padrão que vale memorizar. O endpoint de token aceita um cert de cliente. O access token emitido carrega uma claim cnf.x5t#S256 — o thumbprint SHA-256 desse certificado, em base64url. Resource servers verificam o token e comparam o thumbprint com o certificado que o cliente acabou de apresentar na camada TLS:
{
"sub": "cred_01HZX7…",
"aud": "api.acme.com",
"exp": 1773292800,
"scope": "orders.write",
"cnf": { "x5t#S256": "Y9C2lkF7v…" }
}Você mantém a ergonomia de Bearer — cache, verificação via JWKS, header opaco — e ganha o vínculo na camada TLS de graça. Um token capturado num log é inútil em qualquer conexão que não consiga apresentar o certificado correspondente.
A RFC 8705 chama isso de "Mutual TLS Client Certificate-Bound Access Tokens", e é o ponto certo para quase todo caso M2M que cresceu além do Bearer puro.
Por que isso é difícil em todo lugar
mTLS é famoso por ser correto e infame por ser impossível de entregar. Pra fazer você mesmo, precisa:
- Operar uma autoridade certificadora — emitir, assinar, distribuir, rotacionar, aposentar.
- Distribuir trust bundles pra todo resource server sem quebrar um deploy.
- Construir renovação que não derruba tráfego quando um cert flipa.
- Vincular tokens a certs pra que um único vazamento não desfaça todo o esforço.
- Fazer a mesma coisa duas vezes — uma pro data plane, outra pras admin keys que você usa pra gerenciar tudo isso.
A maioria dos times ou pula mTLS de vez e aceita o risco de vazamento do Bearer, ou monta metade numa sexta-feira e convive com a gambiarra pra sempre. As plataformas que oferecem mTLS normalmente deixam a CA e a distribuição de trust por sua conta — exatamente a parte que quebra em produção.
Essa é a lacuna. mTLS não é um problema difícil de criptografia. É um problema difícil de operação, e por isso a maior parte da auth M2M ainda é Bearer com os dedos cruzados.
Por que o Authaz é o lugar certo pra fazer mTLS
A gente trata mTLS como primitiva de primeira classe da plataforma, não como uma flag enxertada no OAuth. Isso te dá quatro coisas que você não consegue montando do zero:
- Uma CA de workload por tenant, com zero ops. Você não opera infraestrutura de cert. O Authaz emite, rotaciona, aposenta e distribui — entre regiões, atrás de uma única trust URL que seus serviços pinam uma vez. Seu código nunca precisa saber o que é um arquivo PEM.
- Um único modelo de confiança pra tokens e admin keys. A maioria dos fornecedores te dá mTLS no endpoint de token OAuth, ou na management API deles — nunca os dois. O Authaz te dá os dois com a mesma semântica de vínculo. Uma admin key vazada tem o mesmo raio de explosão que um token de data plane vazado: zero, se o cert não estiver junto. Esse é o ponto inteiro do mTLS, e ele não deveria parar no data plane.
- Identidade de workload embutida. Todo cert que o Authaz emite carrega uma identidade SPIFFE no SAN — construída pelo servidor, impossível de adulterar. Seus resource servers param de perguntar "esse token é válido?" e começam a perguntar "esse é o chamador certo pra esse recurso?" — uma pergunta que Bearer não responde.
- Opt-in por credencial. mTLS não é uma chave global que você liga numa sexta-feira e reza. Promova uma integração sensível — seu webhook de billing, a API key do seu agente, a credencial que chama seu endpoint mais regulado — pra vínculo com cert, enquanto o resto continua em Bearer. Sem migração big-bang. Sem rollout acoplado. Sem mudança de SDK pro time todo.
Bearer continua sendo o default. mTLS está a uma flag de distância nas credenciais que justificam. Sua SDK não muda. Seu código não muda. A única coisa que muda é o que acontece quando uma credencial vaza: nada.
O que a gente já absorveu por você
Quando os times montam mTLS por conta, os mesmos cinco erros aparecem mais ou menos nessa ordem — cada um já custou uma indisponibilidade ou um incidente pra alguém:
- Pinar um leaf cert em vez de um trust anchor, e então sofrer uma indisponibilidade planejada na próxima rotação.
- Rotacionar na expiração em vez de antes, vendo o tráfego cair silenciosamente enquanto os certs flipam no meio do voo.
- Esticar a vida dos certs pra evitar a dor de rotação, desfazendo o modelo de segurança pra escapar do operacional.
- Colocar PII num SAN pra facilitar auditoria, e então vazar tudo em logs TLS e em certificate transparency.
- Cabear vínculo de cert no data plane e esquecer do control plane, onde a credencial de maior raio de explosão que você tem segue nua.
Não são teóricos. São o motivo pelo qual a maioria dos times não entrega mTLS mesmo sabendo que deveria. Os defaults do Authaz fazem deles erros que você não precisa cometer — e nunca precisa pensar a respeito.
TL;DR
Bearer responde "essa credencial é válida?" Bom o suficiente pra sessões de navegador e pra maior parte da auth voltada ao usuário.
mTLS responde "essa credencial é válida e o chamador possui a chave à qual ela está vinculada?" Essa é a resposta que você quer em toda superfície de máquina em que uma credencial vazada significaria um takeover — das suas chamadas entre serviços aos seus agentes de IA até as admin keys que mutam sua configuração.
A maioria dos times pula mTLS porque o caminho pra "correto" passa por operar a própria CA, distribuir trust em escala, e rotacionar certs sem derrubar uma request. O Authaz tira tudo isso do seu prato — uma flag por credencial, ergonomia de Bearer por cima, prova de posse vinculada ao TLS por baixo, mesmo modelo de confiança pro seu data plane e pro seu control plane.
Se uma credencial vazada nunca deveria equivaler a um takeover na sua stack, vem conversar com a gente. A gente te mostra como mTLS fica quando as partes difíceis já estão prontas.