mTLS e assinatura de requisição: provando quem chama a API

GuiaUNIFOKAL7 min de leituraProduto e integração
Ver em Markdown

Como o servidor prova quem está chamando: TLS mútuo pela RFC 8705, token preso ao certificado, assinatura de mensagem pela RFC 9421 e o buraco que o proxy que termina o TLS abre no meio.

O caminho de volta já é assunto resolvido: quando o evento chega ao seu servidor, existe assinatura no cabeçalho, janela de tempo e comparação em tempo constante, como descrito em webhooks em KYC e integração segura. O caminho de ida costuma ficar num chaveiro de ambiente com um segredo longo dentro, e ninguém volta a pensar nele.

A pergunta do caminho de ida é outra. Não é como o cliente confere que o servidor é autêntico, do que o TLS já cuida. É como o servidor prova que quem está do outro lado é mesmo o cliente cadastrado, e não alguém que copiou um segredo de um log.

Duas especificações do IETF respondem por caminhos diferentes: a RFC 8705, de fevereiro de 2020, sobre autenticação de cliente por TLS mútuo e tokens presos ao certificado, e a RFC 9421, de fevereiro de 2024, sobre assinatura da mensagem HTTP. A diferença entre elas aparece no ponto em que a maioria das arquiteturas quebra.

O segredo compartilhado e o que ele não prova

A RFC 8705 abre reconhecendo o ponto de partida: o OAuth 2.0 define um método de autenticação de cliente por segredo compartilhado e permite definir mecanismos adicionais. O documento apresenta o TLS mútuo como um desses mecanismos, com a justificativa de que ele oferece características de segurança melhores do que segredos compartilhados.

O mesmo vale para o token emitido. Um token do tipo bearer, na definição da RFC 6750 citada pela própria RFC 8705, pode ser usado por qualquer parte que o tenha em mãos. Quem copiou, usa. É por isso que a rotação de chave de API e de segredo de webhook vira tarefa recorrente.

Duas formas de amarrar certificado ao cliente

A RFC 8705 define dois métodos, e a escolha entre eles não é de gosto.

No método de infraestrutura de chaves públicas, registrado com o valor tls_client_auth, o servidor valida a cadeia do certificado e compara um único nome do sujeito, no formato de nome distinto ou de nome alternativo, com o valor configurado para aquele cliente. A vantagem operacional é a rotação: o cliente troca o certificado obtendo outro com o mesmo sujeito junto à autoridade certificadora, sem alterar nada no servidor de autorização.

No método de certificado autoassinado, registrado como self_signed_tls_client_auth, o cliente registra seus certificados via jwks ou aponta um jwks_uri, e a cadeia não é validada. A autenticação vale se o certificado apresentado no handshake for um dos certificados registrados. Esse método dispensa manter uma infraestrutura de chaves públicas, e a rotação sai pelo jwks_uri.

Nos dois casos a especificação exige que o cliente envie o seu identificador na requisição, para o servidor localizar a configuração esperada antes de olhar o certificado. Sem certificado, ou com certificado que não bate, a resposta é o erro de cliente inválido do OAuth 2.0.

Token preso ao certificado

A segunda metade da RFC 8705 é independente da primeira. Quando o cliente usa TLS mútuo na conexão com o endpoint de token, o servidor de autorização pode prender o token emitido ao certificado.

Em token no formato JWT, isso é representado pelo membro de confirmação x5t#S256, que carrega o resumo SHA-256 da codificação DER do certificado. A obrigação do outro lado é explícita: o recurso protegido precisa obter da camada TLS o certificado usado na conexão e verificar que ele corresponde ao certificado associado ao token. Se não corresponder, a tentativa de acesso é rejeitada com código 401 e erro de token inválido.

Dois efeitos práticos entram no plano antes de ligar isso. O recurso protegido não precisa validar a cadeia de confiança do certificado do cliente, porque ali o TLS mútuo serve apenas como prova de posse da chave privada. E atualizar o certificado invalida os tokens presos a ele, caso que a especificação sugere tratar como token expirado.

O proxy que termina o TLS

Aqui está o buraco que a maioria das arquiteturas tem e não documenta. A RFC 8705 permite que o servidor de autorização ou o servidor de recursos terminem o TLS num balanceador de carga, proxy reverso ou outro intermediário. E então diz, na mesma seção, que o modo como os metadados do certificado do cliente são comunicados com segurança entre o intermediário e o servidor de aplicação está fora do escopo da especificação.

Fora do escopo significa que essa parte é sua. Se o cabeçalho que o proxy injeta com os dados do certificado puder ser forjado por quem alcança a rede interna, toda a prova criptográfica do handshake vira um campo de texto confiável por convenção.

A RFC 9421 descreve o mesmo problema pelo outro lado: o TLS garante integridade e autenticidade apenas sobre uma conexão, e o caminho entre cliente e aplicação pode ser composto por várias conexões independentes, por exemplo quando a aplicação está atrás de um gateway que termina o TLS. Nesses casos, diz o texto, o TLS não garante integridade nem autenticidade fim a fim.

Assinar a requisição, não só a conexão

A RFC 9421 define uma assinatura destacada sobre componentes escolhidos da mensagem HTTP, transportada nos campos Signature-Input e Signature. Quem assina escolhe o que entra na base da assinatura, e regras estritas de canonicalização permitem que a verificação sobreviva a transformações legítimas de intermediários.

Os parâmetros da assinatura carregam o resto. O parâmetro created, cuja inclusão é recomendada, marca o momento da assinatura; expires define o limite de validade; nonce leva um valor único por assinatura; keyid identifica a chave; alg identifica o algoritmo; e tag permite marcar a assinatura por aplicação.

A seção de segurança explica por que esses parâmetros existem. Como a assinatura cobre apenas partes da mensagem, duas mensagens diferentes podem validar contra a mesma assinatura, e o caso extremo é uma assinatura sobre nenhum componente, colável em qualquer requisição. A contramedida é cobrir componentes suficientes para distinguir a mensagem, usar nonce contra repetição e usar created e expires para limitar a utilidade de uma assinatura capturada. É o mesmo raciocínio da janela anti-replay em webhook, na direção oposta.

Um detalhe some com facilidade: a especificação não define meio de cobrir diretamente o corpo da mensagem, e depende de um resumo do conteúdo em campo próprio, que entra entre os componentes cobertos. Assinar cabeçalhos e esquecer esse resumo deixa o corpo livre para troca.

O que decidir antes de escolher

Antes de optar por um caminho ou pelos dois, quatro pontos tirados do próprio texto das especificações.

  • Se o TLS termina em intermediário, documente como o certificado do cliente chega ao servidor de aplicação sem poder ser forjado. A RFC 8705 deixa isso fora do escopo.
  • No método de infraestrutura de chaves públicas, limite as âncoras de confiança. A RFC 8705 alerta que um atacante pode tentar se passar pelo cliente com certificado de mesmo sujeito emitido por outra autoridade aceita pelo servidor.
  • Não escreva validação própria de X.509. A especificação recomenda usar biblioteca estabelecida.
  • Considere expor endpoints separados para o tráfego com TLS mútuo. A RFC 8705 define metadados de alias justamente porque pedir certificado ao cliente pode causar comportamento indesejado em clientes convencionais.

Qualquer que seja a escolha, ela precisa aparecer na documentação da API com o mesmo detalhe com que aparece no código.

Perguntas frequentes

TLS mútuo substitui a chave de API?

Ele substitui o segredo compartilhado como prova de identidade do cliente perante o servidor de autorização. A RFC 8705 o apresenta como mecanismo adicional ao método por segredo do OAuth 2.0, com características de segurança melhores.

Se eu uso TLS mútuo, ainda preciso assinar a requisição?

Depende do caminho. A RFC 9421 observa que o TLS garante integridade e autenticidade apenas sobre uma conexão, e que o caminho pode ter várias conexões independentes. Com gateway terminando o TLS, a garantia fim a fim vem da assinatura da mensagem.

Trocar o certificado derruba a integração?

Troca de certificado invalida os tokens presos a ele. A RFC 8705 recomenda tratar esse caso como token expirado: o cliente pede outro e repete a requisição.

Fontes citadas