Resumo

  • Com DPoP, possuir apenas o valor do access token deixa de bastar: quem o apresenta precisa criar uma prova nova com a chave privada correspondente à impressão digital pública vinculada ao token.
  • A prova cobre um conjunto limitado: método, URI sem query ou fragmento, instante de criação, identificador, hash do token e, quando exigido, um nonce do servidor. Ela não assina toda a requisição nem concede a operação.
  • O resource server ainda precisa validar emissor, audience, prazo, revogação e privilégios do token, coordenar estado antirreplay, reconstruir a URI externa e autorizar sujeito, cliente, recurso, ação e estado atual.

O furto que a posse da chave consegue conter

Um access token aparece por engano em um arquivo de diagnóstico. No modelo Bearer, a sequência de caracteres é a credencial: quem a obtém e alcança um servidor que a aceita pode exercer a autoridade representada até a expiração ou revogação.

O token vazado, porém, está vinculado por DPoP. O invasor o copia para outro cliente e faz uma chamada. O resource server espera o token sob o esquema DPoP e uma prova assinada à parte. O invasor não possui a chave privada correspondente ao thumbprint gravado no token. Uma prova feita com outra chave não coincide. Uma prova antiga capturada falha na comparação de método, URI, tempo, nonce ou replay.

O ganho é concreto: exfiltrar somente o token já não fornece uma credencial completa fora do ambiente de assinatura.

Em seguida, o cliente legítimo envia sua chamada. A assinatura é válida; htm, htu e ath conferem; a prova é recente; o jti não foi visto. Ainda assim, a operação pode ser negada. O token talvez tenha sido emitido para outro audience, contenha apenas leitura, pertença a uma conta suspensa ou tente alterar um objeto que já entrou em estado terminal.

Nenhum desses resultados invalida o DPoP. A restrição de remetente respondeu se aquela chave foi usada nesta requisição. A autorização responde se esse contexto pode produzir esse efeito agora. Um sistema maduro preserva as duas respostas, inclusive quando a primeira é positiva e a segunda negativa.

Tirar do bearer token sua portabilidade automática

A RFC 6750 define o caráter bearer: a parte que possui o token pode usá-lo sem demonstrar posse de uma chave criptográfica. TLS, armazenamento seguro, vida curta e audience restrito reduzem vazamento e impacto, mas o valor copiado continua portátil dentro da superfície que o aceita.

A RFC 9449 introduz uma prova na camada de aplicação. O cliente escolhe um par de chaves assimétricas. No token endpoint, envia um JWT DPoP assinado. O authorization server pode vincular access e refresh tokens ao thumbprint JWK da chave pública. No recurso protegido, cada chamada traz o token e uma prova recém-assinada; o servidor confere se a chave da prova é aquela à qual o token foi vinculado.

A RFC 9700 recomenda tokens sender-constrained, restrição de audience e menor privilégio como controles separados. Isso é decisivo. Vincular a chave não transforma um token destinado ao serviço A em credencial válida no serviço B. Não converte read em write. Não cria consentimento e não torna eterna uma autorização que mudou depois da emissão.

O thumbprint JWK é um digest determinístico de campos canônicos da chave pública. Ele reconhece material criptográfico; não identifica automaticamente pessoa, empresa, postura do dispositivo, intenção jurídica ou mandato. A precisão matemática do identificador não autoriza elevá-lo a uma camada de realidade que ele não descreve.

O conteúdo exato de uma prova

Uma DPoP proof é um JWT assinado como JWS. O protected header usa o tipo explícito dpop+jwt, informa um algoritmo assimétrico aceito pela política local e inclui a JWK pública, nunca material privado. Assim, o receptor aplica um perfil de validação próprio, em vez de enviar qualquer JWT a uma rotina genérica de “assinatura válida”. A RFC 8725 reforça que tipo, algoritmo, issuer, audience e regras pertencem à aplicação, não ao objeto assinado.

O payload básico traz htm, o método HTTP; htu, a URI de destino sem query e fragmento; iat, o momento da criação; e jti, um identificador de prova com imprevisibilidade suficiente para tornar colisões desprezíveis.

Quando há access token, a prova também contém ath, o hash SHA-256 do valor exato do token apresentado. O servidor recalcula esse valor. Uma prova capturada ao lado de um token não pode ser combinada com outro token, mesmo que ambos pertençam à mesma proof key.

Se o servidor tiver desafiado o cliente com DPoP-Nonce, a prova seguinte leva esse nonce. Um desafio atual e imprevisível reduz o valor de um estoque de provas pré-geradas e mostra que a capacidade de assinatura respondeu a uma escolha recente daquele servidor.

Nada disso se aplica sozinho. O receptor rejeita cabeçalhos DPoP múltiplos, JWT malformado, ausência de claims obrigatórios, tipo errado, algoritmo fora da política, assinatura inválida e JWK com material privado. Depois compara método e URI, impõe a janela de tempo, recalcula ath, verifica o thumbprint vinculado ao token e executa a política de nonce que ele mesmo emitiu. Ter os nomes certos não equivale a cumprir as verificações.

Três vínculos, três pontos de falha

O primeiro é token-to-key. O authorization server registra o thumbprint da JWK, em geral no membro cnf.jkt. O resource server compara esse valor com o thumbprint da chave pública que validou a prova atual.

O segundo é proof-to-token. O claim ath compromete a prova com o access token exato desta chamada. Ele impede a substituição do token, mas não confirma audience, escopo, vigência ou revogação.

O terceiro pode anteceder a emissão. O parâmetro opcional dpop_jkt vincula o authorization code à chave que o cliente pretendia usar. Sem isso, quem intercepta o código e dispõe dos demais elementos de resgate pode tentar trocá-lo com uma chave própria e receber um token perfeitamente vinculado — ao atacante. Essa defesa soma-se a PKCE, autenticação do cliente e autorização do resource owner; não substitui nenhuma delas.

Uma instalação pode acertar um vínculo e omitir outro. Por isso, a evidência deve nomear a fase e a comparação, não resumir o sistema com um campo “PoP habilitado”.

A requisição provada é menor que o comando de negócio

Os claims htm e htu tornam a prova específica para uma parte da requisição. Uma prova para GET não deve valer como DELETE; uma prova para um destino não deve ser levada a outro.

O limite é igualmente importante. Pela especificação, htu exclui query e fragmento. A base do DPoP também não assina corpos nem cabeçalhos arbitrários. Se POST /transfer?account=A e POST /transfer?account=B têm o mesmo htu, a prova não distingue a conta. Se o JSON muda valor ou beneficiário, a assinatura não passa a cobrir esses dados.

Não se trata de uma falha escondida, mas de divisão de trabalho. DPoP restringe o remetente; não substitui HTTP Message Signatures, autorização de transação ou idempotência. A RFC 9110 define a semântica HTTP, enquanto o serviço continua responsável pelo significado de query, body e estado do objeto.

Proxies tornam o limite operacional. O cliente prova o esquema, a autoridade e o caminho externos que enxerga. Um gateway pode terminar TLS, reescrever o caminho e encaminhar um host interno. Se o backend reconstruir htu do lado errado, rejeita clientes corretos; se aceitar variantes frouxas para evitar erro, amplia a superfície de replay. O contexto externo, a cadeia confiável de forwarding e a normalização precisam de dono e testes reproduzíveis.

Frescor depende de estado distribuído

iat e jti permitem detectar replay, mas não rejeitam nada por si. O receptor escolhe idade aceitável, tolerância de relógio, chave de replay, período de retenção e topologia de compartilhamento.

Se uma região grava o jti e outra ainda não recebeu o estado, a mesma prova pode passar duas vezes. Se check e write não forem atômicos, cópias simultâneas podem observar “não usado”. Se o nonce for estático, duradouro ou compartilhado além do necessário, deixa de indicar uma interação atual.

Nonces do authorization server e do resource server pertencem a emissores diferentes. Um cliente com vários servidores deve indexá-los por origem; devolver o desafio de um ao outro cria erro, não frescor.

Rejeitar uma prova repetida também não garante execução exactly-once. O cliente pode gerar duas provas novas para retries. Ambas são únicas e legítimas. Se a primeira cobrança foi confirmada no banco e a resposta se perdeu, só a idempotência de negócio, o registro de commit e a reconciliação decidem o efeito da segunda.

Posse da chave não é identidade nem consentimento

A JWK pública informa qual chave verificou a assinatura. Não informa quem a gerou, quem controla o processo, se pertence a um cliente registrado ou se o usuário aprovou a operação. Confidential-client authentication pode coexistir com DPoP. O token fornece o contexto emitido pelo authorization server. A prova fornece sender constraint. A aplicação fornece a última decisão.

No navegador, esse cuidado é essencial. A RFC 10017 descreve como uma chave não extraível dificulta copiar token e chave para uso posterior em outro ambiente. Entretanto, JavaScript malicioso já executando na mesma origem pode pedir ao navegador que assine, enviar chamadas na sessão viva ou iniciar novo fluxo de autorização. Uma chave que não sai ainda pode ser usada pelo código errado no lugar certo.

Se o atacante obtiver token e chave, ou controle duradouro da interface de assinatura, a restrição se desfaz. Chaves em hardware e não extraíveis melhoram custódia; não transformam cliente comprometido em principal confiável.

Evidência que percorre o caminho inteiro

A IANA registra DPoP e DPoP-Nonce como campos HTTP permanentes e mantém os parâmetros OAuth relacionados. O vocabulário comum é necessário, mas não demonstra que uma instalação compara a mesma URI, coordena replay ou preserva a decisão do recurso.

O rastro deve unir emissão e consequência. Sem registrar token cru ou chave privada, preservar hash seguro da prova, thumbprint, token type, resultado de ath, URI externa e interna normalizadas, idade, emissor do nonce, resultado do replay store, issuer e audience do token, decisão de escopo, versão da política e identificador final de commit.

Os testes negativos atravessam componentes: token copiado sem chave; chave correta com token errado; prova antiga; jti repetido; método incorreto; rewrite externo-interno; nonce ausente ou estrangeiro; audience errado; escopo insuficiente; conta suspensa; operação já concluída. Explicar cada recusa vale mais que exibir um selo verde de DPoP.