DevSecOps & Supply Chain
OAuth em 2026: PKCE, BFF e os testes que evitam roubo de tokens
Entenda a RFC 10017, compare BFF e tokens no navegador e organize testes de PKCE, audiência e renovação com evidências de laboratório.
Segurança OAuth começa no caminho percorrido pelo token. Um login pode funcionar perfeitamente e ainda entregar credenciais a um destino incorreto, aceitar um código fora da sessão original ou expor um refresh token ao JavaScript. Para avaliar uma implementação, o teste precisa acompanhar emissão, armazenamento, uso e revogação.
Em agosto de 2026, a IETF publicou a RFC 10017 sobre aplicações OAuth no navegador. Ela oferece um ponto de partida atual para discutir arquitetura. Este guia combina essa referência com os padrões de PKCE, OpenID Connect e proteção de tokens, e propõe uma matriz de testes para um laboratório autorizado.
PKCE em bytes: por que o código roubado não basta
A comparação de PKCE deve ser entendida no nível dos bytes. O cliente cria um code_verifier aleatório para uma única transação; o desafio S256 é o SHA-256 desse texto, codificado em base64url sem padding. O servidor guarda o desafio junto do código de autorização. Na troca, recalcula o desafio do verificador recebido e compara os valores. Quem possui apenas o código não conhece automaticamente o verificador.
O exemplo local a seguir é uma implementação didática da transformação, não o código de um provedor. Os nomes correspondem aos parâmetros de RFC 7636. O par gerado é efêmero: não deve ser reutilizado entre usuários, abas ou tentativas. Um código interceptado junto do verificador continua perigoso; PKCE não corrige armazenamento que expõe ambos.
import base64
import hashlib
import secrets
verificador = secrets.token_urlsafe(32)
desafio = base64.urlsafe_b64encode(
hashlib.sha256(verificador.encode('ascii')).digest()
).rstrip(b'=').decode('ascii')
assert 43 <= len(verificador) <= 128
assert len(desafio) == 43
print('S256', len(verificador), len(desafio))
Linha a linha, a aleatoriedade produz o segredo da transação, o hash evita enviá-lo no pedido de autorização e a codificação converte o digest em um valor apropriado para o protocolo. A expressão rstrip remove somente padding. Usar base64 comum, incluir padding ou aplicar hash sobre bytes diferentes do verificador original pode quebrar a interoperabilidade.
Há outras associações indispensáveis: código ao cliente, redirect URI, sessão que iniciou a operação e uso único. Uma boa matriz local troca apenas um desses vínculos por vez. O caso válido deve funcionar; verificador incorreto, código reutilizado e callback de outra transação devem falhar sem emissão de token. state e nonce têm finalidades próprias e não são substituídos pela aritmética de PKCE.
No BFF, a fronteira muda: o navegador recebe uma sessão, enquanto o backend manipula tokens. Teste também CSRF, isolamento da sessão e autorização das chamadas encaminhadas. Um BFF que aceita qualquer destino ou devolve tokens no corpo reintroduz a superfície que pretendia reduzir. A investigação precisa seguir onde o segredo existe, quem o lê e qual identidade a API realmente autoriza.
OAuth, OpenID Connect e JWT têm papéis diferentes
OAuth trata da autorização para acessar recursos. OpenID Connect acrescenta uma camada de identidade, com o ID token e regras de validação próprias. JWT é um formato que pode transportar declarações. Encontrar três partes separadas por pontos não demonstra que o token é confiável ou que ele pertence à aplicação que o recebeu.
Na revisão de um login OpenID Connect, confirme a assinatura com as chaves do emissor confiável, o emissor esperado, a audiência e a validade temporal. Quando a requisição utiliza nonce, o valor recebido precisa corresponder à transação iniciada. As condições exatas dependem do fluxo. A especificação OpenID Connect Core detalha essas verificações. Um ID token não deve ser tratado automaticamente como um access token para qualquer API.
O que PKCE protege e o que continua exposto
PKCE vincula a troca do código de autorização a um segredo temporário criado pelo cliente. No método S256, o cliente envia um desafio derivado desse segredo e apresenta o valor original ao trocar o código. O servidor compara os valores antes de liberar tokens. Esse vínculo dificulta o aproveitamento de um código interceptado por quem não possui o verificador. O mecanismo é definido na RFC 7636.
Cliente cria o verificador
↓
Autorização recebe o desafio S256
↓
Callback recebe um código temporário
↓
Troca exige o verificador correspondente
↓
Servidor valida o vínculo e emite tokens
Em um ambiente de teste, tente trocar um código com o verificador de outra sessão e registre a rejeição. Verifique também o tratamento de código já utilizado. Esses casos examinam o vínculo da transação; não comprovam, sozinhos, que armazenamento, redirecionamento e autorização da API estejam seguros.
PKCE não elimina XSS e não impede que código malicioso execute ações dentro de uma sessão comprometida. Portanto, a pergunta seguinte deve ser: qual componente recebe o token e quais operações ele consegue realizar?

Ilustração conceitual gerada: navegador com sessão, BFF responsável pelos tokens e API. Os objetos ajudam a separar responsabilidades; não representam uma sequência normativa de mensagens OAuth.
BFF ou tokens no navegador: uma decisão de arquitetura
No padrão Backend for Frontend, ou BFF, um componente do servidor assume o papel de cliente OAuth, mantém tokens e encaminha as chamadas da interface à API. O navegador trabalha com uma sessão baseada em cookie. A RFC 10017 compara esse modelo com um backend que entrega access tokens ao navegador e com o cliente inteiramente executado no navegador.
Manter os tokens no servidor reduz sua exposição ao JavaScript, mas um script malicioso ainda pode tentar realizar ações pela sessão do usuário. O BFF também exige controles de sessão, proteção contra CSRF e limites nas rotas encaminhadas. A escolha deve considerar os recursos da equipe, a sensibilidade das operações e a capacidade de manter esse componente.
Cookie seguro e autorização da API precisam de retestes separados
No BFF, o navegador recebe uma sessão e o servidor guarda os tokens. Essa escolha reduz a exposição dos tokens ao JavaScript, mas cada proteção do cookie responde a uma pergunta específica. Secure restringe o envio ao transporte seguro; HttpOnly limita o acesso por script; SameSite influencia o envio em contextos entre sites. Nenhuma dessas opções, sozinha, confirma que a operação pertence ao usuário autenticado. A referência de gerenciamento de sessões da OWASP ajuda a separar essas responsabilidades.
No laboratório, mantenha duas identidades fictícias e registre qual sessão o BFF associou a cada chamada da API. Verifique renovação, logout, isolamento entre contas e operações que alteram estado. Um cookie inacessível ao script não impede que um XSS use a sessão pelo próprio navegador; o relatório precisa distinguir extração de token de execução de uma ação autenticada.
Para CSRF, documente o comportamento esperado conforme o desenho da aplicação e seus fluxos entre sites. Preserve o resultado da validação de origem ou do mecanismo adotado, sem exportar cookies e tokens. O encerramento exige evidência de que o cliente perdeu a sessão quando previsto e de que o BFF continua aplicando autorização a cada recurso.
Refresh tokens, audiência e prova de posse
A RFC 9700, a prática de segurança atual para OAuth 2.0, orienta restringir privilégios e reduzir o uso indevido de tokens roubados. Para clientes públicos, refresh tokens precisam usar rotação ou vínculo ao remetente. Também é necessário analisar audiência, escopo e o comportamento quando um token antigo reaparece.
Uma política operacional precisa responder a quatro perguntas: qual recurso aceita o token, quanto tempo ele permanece útil, como a renovação é protegida e quais credenciais são revogadas quando ocorre um incidente. Expiração curta não substitui a rejeição de audiência incorreta.
DPoP acrescenta uma prova assinada associada à chave do cliente e à requisição. A RFC 9449 descreve o mecanismo para restringir tokens ao remetente. Ele aumenta os requisitos de validação e de tratamento de replay. Não significa que uma aplicação com JavaScript comprometido possa continuar sendo considerada confiável.
Uma matriz de testes para o laboratório Aurora
O cenário abaixo é fictício e constitui um roteiro proposto, não um relato de exploração. Considere duas sessões de teste, uma API de perfil e dados sem valor real. Para cada caso, guarde o resultado esperado e a resposta observada.
| Caso controlado | Resultado esperado | Evidência útil |
|---|---|---|
| Verificador PKCE de outra sessão | Troca recusada | Status e erro do endpoint de token |
| Callback que não está cadastrado | Autorização recusada | Destino solicitado e política efetiva |
| Token destinado a outra API | Acesso recusado | Audiência esperada e resposta |
| Renovação com token anterior | Aplicação da política de replay | Eventos da família de tokens |
| Logout seguido de chamada autenticada | Comportamento coerente com a política publicada | Janela de validade e estado da sessão |
O relatório deve ocultar códigos e tokens completos. Prefira hashes de correlação, horários, identificadores de sessão fictícios e trechos mínimos das respostas. Uma captura isolada mostra um comportamento; a comparação entre sessões ajuda a explicar a fronteira que deveria ter sido respeitada.
Perguntas frequentes
PKCE substitui o segredo de um cliente confidencial?
Não. PKCE protege o vínculo da troca do código. A autenticação de um cliente confidencial continua sendo uma responsabilidade separada.
Decodificar um JWT prova que sua assinatura é válida?
Não. Decodificação apenas permite ler a estrutura. A validação precisa aplicar assinatura, emissor, audiência e as demais condições esperadas para aquele uso.
Como organizar as evidências no Android?
Comece pelo guia de JWT e evidências no CatSuite e pelo roteiro de comparação de requests no Repetir. Trabalhe apenas com ambientes sob autorização e preserve os registros do servidor de identidade para correlacionar os resultados.
Próximo passo: desenhe o percurso de um único login e marque todos os componentes que recebem código, token ou cookie. Essa representação torna os testes mais objetivos do que uma lista de parâmetros examinados sem contexto.