Por Thiago Duda

Introdução ao OAuth 2.0

O OAuth (Open Authorization) é um protocolo de autorização que permite que aplicações acessem recursos de terceiros em nome dos seus usuários de forma segura. Uma das principais vantagens é a sua capacidade de facilitar a integração entre sistemas sem a necessidade de compartilhar as credenciais do proprietário do recurso solicitado, utilizando tokens de acesso como intermediários. Notavelmente, é amplamente utilizado no login, através de redes sociais, permitindo que usuários acessem diferentes aplicações com suas contas do Google, Facebook ou outras plataformas, sem a necessidade de criar credenciais para cada aplicação.

Antes de discutirmos sobre as vulnerabilidades, é essencial entendermos os componentes fundamentais e como eles interagem durante o processo de autorização. O primeiro componente que abordaremos são as roles, que definem os principais atores no fluxo de autorização. São elas:

  • Resource Owner: é o usuário/entidade proprietário dos dados, ou recursos que serão solicitados e concedidos se autorizados;
  • Resource Server: servidor que protege e concede acesso aos dados, com base na validade dos tokens de acesso gerados pelo servidor de autorização e nas autorizações feitas pelo proprietário do recurso;
  • Client: é a aplicação que solicita acesso aos recursos/dados permitidos pelo usuário;
  • Authorization Server: é o servidor responsável por emitir os tokens de acesso, após a devida autenticação e consentimento do usuário.

Grant Types: O que são e para que servem?

Existem quatro principais grant types no OAuth 2.0, cada um adequado a diferentes casos de uso:

  1. Authorization Code: é o mais utilizado em aplicações web. A aplicação cliente redireciona o usuário para o servidor de autorização, onde ele se autentica e consente com os dados que serão compartilhados. O servidor de autorização emite um código de autorização, que é trocado por um token de acesso pelo cliente. Esse método é considerado seguro, pois a troca de tokens ocorre no back-end da aplicação cliente, sem expor o token diretamente ao navegador;
  2. Implicit: comumente utilizado em SPAs e aplicações móveis. Aqui, o token de acesso é emitido diretamente ao cliente sem um código de autorização intermediário. Esse método, no entanto, é menos seguro em comparação ao Authorization Code, pois o token é entregue diretamente ao navegador do usuário, aumentando a possibilidade de interceptação;
  3. Resource Owner Password Credentials (ROPC): utilizado em casos em que o cliente e o servidor de autorização pertencem ao mesmo domínio. O proprietário do recurso fornece diretamente suas credenciais (usuário e senha) ao cliente, que as utiliza para obter um token de acesso. Embora simples, essa abordagem é considerada insegura e obsoleta em muitos cenários modernos;
  4. Client Credentials: esse tipo de concessão é utilizado em cenários de machine-to-machine (M2M), no qual o cliente solicita um token de acesso diretamente ao servidor de autorização, sem envolver um usuário final. Isso é comum em integrações de serviços e automações internas, onde não há interação com usuários.

O objetivo deste artigo é focar no grant type comumente encontrado em aplicações webAuthorization Code, e abordar os principais problemas de segurança que podem surgir a partir de uma implementação inadequada. Sendo assim, antes de aprofundarmos no seu funcionamento, é importante compreendermos o que cada parâmetro presente na requisição de início do fluxo representa.

Cada parâmetro possui um papel específico. Vamos detalhar os principais deles, com um exemplo prático de como eles normalmente aparecem em um cenário real:

  1. client_id: este parâmetro é o identificador único da aplicação cliente que está solicitando a autorização. Ele é fornecido pelo servidor de autorização no momento do registro da aplicação cliente. O client_id garante que o servidor saiba exatamente qual aplicação está solicitando o acesso e é essencial para associar a requisição de autorização ao cliente correto.
  2. redirect_uri: define o endereço para o qual o código de autorização emitido pelo Authorization Server será enviado.
  3. response_type: indica o tipo de resposta que o cliente espera do servidor de autorização. No caso do fluxo Authorization Code, o valor deste parâmetro deve ser code, informando ao servidor que o cliente deseja receber um código de autorização como resposta. Através dele, conseguimos identificar qual o grant type está sendo utilizado pela aplicação.
  4. scope: define os níveis de acesso que o cliente está solicitando em relação aos recursos do usuário. Cada escopo especifica um conjunto de permissões, como acesso aos dados de perfil, por exemplo.
  5. state: este parâmetro é usado para proteger contra-ataques Cross-Site Request Forgery (CSRF) e garantir a integridade do fluxo. A aplicação cliente gera um valor único e aleatório que será incluído na requisição. Quando o servidor redireciona o usuário de volta, o cliente verifica se o valor do state retornado corresponde ao valor original, mitigando possíveis ataques. Embora não seja estritamente obrigatório, sua ausência ou implementação inadequada pode levar a vulnerabilidades.

Esses são os principais parâmetros que formam a base da requisição de autorização no fluxo Authorization Code. Implementá-los corretamente é essencial para evitar falhas e garantir uma autorização confiável.

Fluxo de funcionamento do Authorization Code

Imagem 1: Fluxo de autorização Authorization Code – Fonte: Portswigger

Como observado na imagem acima, o fluxo de autorização é iniciado através da intenção do usuário de utilizar um serviço OAuth. Sendo assim, a aplicação cliente dá início ao fluxo de autorização solicitando ao usuário que conceda acesso aos seus dados — dentro do escopo pré-definido, através do serviço OAuth escolhido. 

Esta primeira etapa costuma iniciar com a seguinte requisição:

Imagem 2: Exemplo de authorization request – Fonte: Autor

Após o consentimento do usuário, o servidor OAuth o direciona de volta para a aplicação cliente, enviando o código de confirmação. A requisição abaixo ilustra essa etapa:

Imagem 3: Requisição de troca do code – Fonte: Autor

Após estas etapas iniciais, a comunicação passa a ser server-to-server, dessa forma, adicionando uma camada a mais de segurança para troca do token. Sendo assim, o servidor da aplicação cliente irá fornecer o código obtido na etapa anterior para o servidor do serviço OAuth que, por sua vez, irá responder com o token de acesso que será utilizado para solicitar os recursos do usuário. 

Imagem 4: Exemplo de solicitação do token de acesso – Fonte: Autor

Por fim, é feita uma chamada à API para o serviço OAuth, que valida o token de acesso, e sendo válido, retorna os dados requisitados.

Imagem 5: Troca do token pelos dados do usuário – Fonte: Autor

Vulnerabilidades Comuns:

Após construir a base necessária para entender os principais componentes do OAuth e do Authorization Code Grant Type, abordaremos na sequência as principais vulnerabilidades inerentes a sua implementação inadequada.

Redirecionamento Arbitrário (Open Redirect):

Uma das vulnerabilidades mais conhecidas no protocolo e, também, em aplicações web, é o redirecionamento arbitrário, que ocorre quando a URL de redirecionamento redirect_uri fornecida pelo cliente não é devidamente tratada pelo servidor de autorização. Isso permite que um eventual atacante engane o usuário com objetivo direcioná-lo para uma aplicação maliciosa após a autenticação, possibilitando a captura de tokens de acesso.

Imagem 6: Exemplo de requisição com redirect_uri modificado – Fonte: Autor

Em um cenário real, um atacante pode manipular o parâmetro redirect_uri para obter o Authorization Code da vítima. Assim, quando a vítima acessar a página e iniciar o processo de login via OAuth, o code será capturado pelo invasor, o qual poderá em seguida fornecê-lo para o endereço de callback e obter o token.

O fluxo a seguir ilustra o cenário em que a vítima inicia o processo de autorização do OAuth com o parâmetro redirect_uri modificado para um endereço de controle do atacante:

Imagem 7: Redirecionamento para aplicação do invasor – Fonte: Autor

Observe que a requisição com o authorization code da vítima é enviada para a aplicação do atacante:

Imagem 8: Requisição de envio do código da vítima para aplicação do invasor – Fonte: Autor

Em seguida, o atacante inicia o fluxo de autenticação via OAuth e, no momento do envio do code, substitui o valor original pelo valor que foi capturado anteriormente:

Imagem 9: Requisição de troca do authorization code utilizando o código da vítima – Fonte: Autor

Por fim, observe que o ataque foi concretizado, uma vez que o atacante autenticou-se na conta da vítima:

Imagem 10: Imagem que ilustra o sequestro de sessão realizado – Fonte: Autor

Mitigação:

Para mitigar essa vulnerabilidade, as URLs de redirecionamento devem ser restritas a um conjunto predefinido de domínios confiáveis — allow listing, no servidor de autorização. Isso garante que o redirecionamento só ocorra para locais seguros. Além disso, a validação deve ser feita no lado do servidor, evitando que essa informação possa ser facilmente manipulada. Por outro lado, é importante monitorar o acesso ao servidor de autorização, garantindo que o endereço de redirecionamento permitindo não tenha sido manipulado em caso de um eventual comprometimento. É importante salientar que, alguns dos provedores de OAuth mais conhecidos, tais como — Google, Facebook etc — já fazem o tratamento deste parâmetro de maneira adequada. No entanto, em caso de comprometimento de credenciais do servidor de autorização, um atacante poderia adicionar domínios de seu controle à lista de domínios confiáveis, que possibilitariam a execução dos ataques.

CSRF – Implementação inadequada do parâmetro state:

A ausência ou implementação inadequada do parâmetro state em fluxos de autenticação OAuth pode expor aplicações a ataques de Cross-Site Request Forgery, possibilitando que um invasor manipule requisições de autorização e sequestre contas de usuários. Essa vulnerabilidade ocorre quando pelo menos um dos critérios abaixo é atendido:

  • o parâmetro state não é utilizado;
  • é definido com um valor fixo ou previsível;
  • não há validação adequada no servidor;
  • o valor pode ser reutilizado em múltiplas requisições.

Os seguintes passos são utilizados no cenário de ataque:

  1. o invasor inicia o fluxo de vinculação de perfil na aplicação vulnerável e obtém o link de autorização válido, com ausência ou implementação inadequada do parâmetro state;
  2. ele compartilha esse link com a vítima por meio de phishing ou uma aplicação maliciosa;
  3. se a vítima acessar o link, a conta dela na aplicação será vinculada ao perfil OAuth do invasor;
  4. por fim, quando o invasor utilizar a opção “Login com OAuth”, será autenticado na conta da vítima.

Para ilustrar um cenário prático, imagine que uma aplicação permite que usuários vinculem perfis de redes sociais às suas contas utilizando OAuth. No fluxo correto de autorização, a aplicação deve gerar um valor único para o parâmetro state, que será validado pelo servidor ao receber a resposta da autorização. No entanto, se esse parâmetro for ausente, ou mal implementado, um atacante pode explorar essa falha para vincular a conta da vítima ao seu próprio perfil social.

A imagem abaixo ilustra a aplicação com as características narradas acima. Observe que a aplicação possui uma funcionalidade que permite ao usuário vincular um perfil de rede social à sua conta e, posteriormente, utilizá-lo para autenticar-se na aplicação.

Imagem 11: Perfil de usuário vinculado a conta da aplicação – Fonte: Autor

Observe que, no exemplo, o usuário wiener está vinculado ao perfil social peter.wiener. Isso significa que, ao optar por “Login com OAuth”, utilizando a conta peter.wiener, se autenticará na conta wiener. Assim, uma vez que a aplicação não implemente corretamente o parâmetro state o invasor pode compartilhar um link de vinculação de conta com a vítima. Quando a vítima acessa esse link, sua conta é vinculada ao perfil social do atacante.

Para explorar essa falha, o atacante pode iniciar o fluxo de vincular um perfil a sua conta, utilizando para isso suas credenciais do provedor OAuth e, no momento da troca do authorization code, interceptar a requisição e utilizar o valor capturado para realizar o ataque. A imagem abaixo ilustra a captura do code do invasor:

Imagem 12: Interceptação do authorization code – Fonte: Autor

Uma vez obtido o código, o invasor poderia utilizar um iframe em uma aplicação maliciosa e induzir a vítima a acessá-la. Abaixo, o payload utilizado para demonstração do ataque:

Imagem 13: Código de exemplo utilizado para exploração – Fonte: Autor

Por fim, quando a vítima acessar a aplicação maliciosa, sua conta será vinculada ao perfil do invasor. A partir deste momento, quando o invasor optar pela opção “Login com OAuth”, terá acesso à conta da vítima. A imagem abaixo elucida a concretização do cenário de ataque:

Imagem 14: Conta da vítima vinculada ao perfil OAuth do invasor – Fonte: Autor

Mitigação:

Para evitar os riscos supracitados, é essencial adotar boas práticas durante a implementação. A primeira medida é utilizar o parâmetro state de maneira dinâmica, gerando um valor único e não previsível para cada solicitação. Além disso, é fundamental validar o parâmetro corretamente. Adicionalmente, é possível implementar um mecanismo de expiração para os valores do parâmetro. Eles devem ter um tempo de validade limitado e, caso a resposta do servidor de autorização seja recebida após esse período, a solicitação deve ser rejeitada. Isso impede o reaproveitamento de valores antigos, que poderiam ser capturados por atacantes.

Possibilidade de sequestro de contas (Pre-Account Takeover):

Popularmente chamado de Pre-Account Takeover, o cenário que abordaremos a seguir surge a partir da ausência de validação cadastral por aplicações que implementam tanto o OAuth quanto formulários de login/cadastro para gerenciamento de sessão. Essa ausência ocorre quando a aplicação permite o cadastro de um e-mail sem exigir a verificação prévia de sua propriedade como, por exemplo, através de um link de confirmação enviado ao usuário. Isso possibilita que um atacante registre uma conta utilizando o endereço de e-mail de outra pessoa e, posteriormente, essa vítima, ao tentar recuperar ou criar sua conta real, acaba assumindo um perfil já comprometido.

O diagrama abaixo ilustra como é possível realizar o Pre-Account Takeover com objetivo de obter dados sensíveis da vítima:

Imagem 15: Diagrama de ilustração do ataque – Fonte: Autor

Como observado na imagem acima, o passo (1) consiste em realizar o cadastro na aplicação vulnerável, utilizando para isso o e-mail da vítima alvo que está cadastrado no serviço OAuth utilizado pela aplicação. Pelo fato de não existir validação cadastral, a conta será criada automaticamente, mesmo que o atacante não possua controle do e-mail utilizado.

Já na etapa de número (2), a vítima utiliza-se do OAuth para se autenticar na aplicação, utilizando o e-mail como chave de usuário, sendo assim, ao realizar o login tanto pelo OAuth quanto pelo formulário, a conta acessada será a mesma. Este é um fato importante de se observar, haja visto que algumas aplicações podem diferenciar as contas a partir do mecanismo utilizado para sua criação. 

Em seguida, na etapa (3), a vítima — já autenticada — utiliza a aplicação e atualiza dados cadastrais, preenche informações sensíveis como dados bancários, por exemplo etc.

Por fim, na última etapa, o atacante acessa a conta e tem acesso a todos os dados que a vítima, sem conhecimento do comprometimento da conta, preencheu. Em cenários mais críticos, como aplicações e-commerce o ataque pode ter impacto ainda maior, caso o usuário salve dados de cartão de crédito, possua saldo disponível para compras etc. 

Possibilidade de sequestro de contas (Account Takeover)

Diferentemente do cenário anterior, o cenário atual consiste em cadastrar a vítima — já cadastrada na aplicação alvo — no provedor OAuth, considerando que ela não esteja cadastrada nele (serviço de e-mail, por exemplo). Para ficar mais intuitivo, imagine que a vítima esteja cadastrada na aplicação, utilizando o e-mail [email protected]. No entanto, ela não tem cadastro no serviço mail-service.com, e a aplicação permite o login utilizando o OAuth do serviço supracitado. Sendo assim, o atacante se registraria em mail-service.com utilizando o e-mail da vítima, e por fim, utilizaria o OAuth para fazer login na conta do usuário alvo, concretizando o Account Takeover.

O diagrama abaixo tem como objetivo facilitar o entendimento do cenário de ataque narrado acima:

Imagem 16: Diagrama de ilustração do ataque – Fonte: Autor

Observe que, na etapa de número (1) a vítima cadastrou-se na aplicação utilizando [email protected]. Porém, ela não possui cadastro no serviço mail.com, ou optou por excluir sua conta, por exemplo. Sendo assim, o atacante registrou-se no serviço utilizando o mesmo e-mail que a vítima utilizou para se cadastrar na aplicação, como observado na etapa de número (2). Por fim, o atacante utilizou a opção “Login com OAuth” para autenticar-se na aplicação, e concretizar o acesso à conta da vítima.

Mitigação:

Para mitigar os problemas supracitados, a aplicação precisa implementar um mecanismo de validação cadastral, com o  objetivo de validar se o usuário que está se registrando de fato é quem ele diz ser. Além disso, caso o usuário já esteja cadastrado e, a vítima, opte por se autenticar através do serviço OAuth aceito pela aplicação, recomenda-se que sejam enviadas notificações para o usuário sempre que um método de login diferente do habitual for realizado na sua conta, permitindo que ele desvincule, caso não reconheça o acesso.

Conclusão:

O OAuth é uma ferramenta poderosa que facilita a integração e a comunicação entre aplicações, trazendo eficiência e segurança para o compartilhamento de recursos. No entanto, como qualquer tecnologia, sua implementação inadequada pode abrir portas para sérias vulnerabilidades que comprometem a segurança de usuários e aplicações.

Neste artigo, abordamos algumas das falhas relacionadas ao OAuth, como redirecionamento arbitrário, implementação inadequada do parâmetro state, sequestro de contas e vazamento de tokens. Também destacamos as práticas recomendadas para mitigar esses riscos. Além disso, é importante destacar que, embora não tenha sido o foco desta publicação, outros grant types também podem apresentar problemas de segurança se não implementados corretamente. Portanto, é essencial garantir que a implementação do OAuth, independente do grant type utilizado, seja feita dentro dos padrões e recomendações de segurança.

Referências:

BURGESS, PortSwigger Research. OAuth: Exploiting web security. Disponível em: https://portswigger.net/web-security/oauth

HARDT, Dick. The OAuth 2.0 Authorization Framework. IETF, 2012. RFC 6749. Disponível em: https://datatracker.ietf.org/doc/html/rfc6749 

PORT SWIGGER. OAuth: Forced OAuth Profile Linking Lab. Disponível em: https://portswigger.net/web-security/oauth/lab-oauth-forced-oauth-profile-linking

CLOUD SECURITY ALLIANCE. OAuth Token: What it is, How it Works, and its Vulnerabilities. Disponível em: https://cloudsecurityalliance.org/blog/2024/01/09/oauth-token-what-it-is-how-it-works-and-its-vulnerabilities

PORT SWIGGER. OAuth: Authentication Bypass via OAuth Implicit Flow Lab. Disponível em: https://portswigger.net/web-security/oauth/lab-oauth-authentication-bypass-via-oauth-implicit-flow

COUPA SOFTWARE. How to Mitigate OAuth2 Vulnerabilities. Disponível em: https://www.coupa.com/blog/how-to-mitigate-oauth2-vulnerabilities/