Vulnerabilidades no aplicativo de namoro Feeld
(fortbridge.co.uk)- Uma revisão do backend do aplicativo de namoro móvel Feeld encontrou 8 vulnerabilidades, incluindo exposição de perfis, leitura e modificação de mensagens e acesso a anexos de chat; todas, exceto a primeira, se enquadram em Broken Access Control do OWASP Top 10
- Usuários comuns veem apenas informações limitadas na tela do app, mas ao inspecionar as respostas com um proxy era possível obter informações de nível premium, como idade, distância, foto de perfil e
streamUserIdde usuários que enviaram “like” - Várias vulnerabilidades eram encadeadas obtendo identificadores como streamUserId, profileId, messageId e channelID em outras respostas de API e inserindo esses valores como parâmetros de requisição, ampliando o escopo de acesso até mensagens, matches, perfis, likes e envio de mensagens em chats de terceiros
- Os problemas com anexos de chat foram confirmados em fotos comuns, fotos com limite de 5 a 15 segundos, vídeos comuns e vídeos de reprodução única; algumas URLs de Cloudinary e Stream CDN eram acessíveis sem autenticação
- A FORTBRIDGE divulgou os problemas à Feeld em 8 de março de 2024; após vários pedidos de adiamento da publicação, a Feeld respondeu em 16 de agosto de 2024 que havia implementado mudanças para mitigar os itens restantes, e o post do blog foi publicado em 10 de setembro de 2024
Escopo das vulnerabilidades encontradas no Feeld
- O alvo é o aplicativo de namoro móvel Feeld, semelhante a Tinder e Bumble, com filtros por distância, idade, gênero, casal e localização
- Usuários premium também podem buscar por tipo de kink, cenários de menáge à trois/grupo e tipo de relacionamento de interesse
- A revisão de segurança identificou 8 vulnerabilidades
- Exposição de informações de perfil para usuários não premium
- Leitura de mensagens de terceiros
- Acesso sem autenticação a anexos de foto e vídeo em chats
- Exclusão, restauração e modificação de mensagens de terceiros
- Atualização de informações de perfil de terceiros
- Recebimento de “Like” em perfis arbitrários
- Envio de mensagens em chats de terceiros
- Visualização dos matches de terceiros
- Com exceção da primeira, os demais problemas se enquadram na categoria Broken Access Control do OWASP Top 10
Informações de perfil expostas a usuários não premium
- Quando um usuário comum vê, no menu Likes, quem curtiu seu perfil, o app mostra apenas algo como o nome e uma foto borrada
- Ao interceptar requisições e respostas com uma ferramenta de proxy como Burp, a resposta continha informações no mesmo nível visto por usuários premium
- Idade
- Distância
- Foto de perfil completa
streamUserId
- As fotos de perfil ficam armazenadas em
res.cloudinary.come podiam ser acessadas sem autenticação - O streamUserId obtido na resposta podia então ser usado na vulnerabilidade de leitura de mensagens de terceiros
Problemas de controle de acesso em mensagens e matches
- Para ler mensagens de terceiros, era necessário o streamUserId da vítima, e esse valor era exposto em várias requisições de API
- Um fluxo de exemplo consistia em obter o streamUserId do usuário-alvo na resposta da requisição GraphQL
DiscoverProfilese depois inserir esse valor na condiçãomemberda requisição de canal de chat - Ao buscar por
"text"na resposta, era possível verificar a quantidade e o conteúdo das mensagens trocadas pela vítima - Pela mesma abordagem, também era possível obter o messageId associado a cada mensagem, usado para excluir, restaurar e modificar mensagens
- Ao alterar o parâmetro vulnerável
profileIddeChatListQuery, era possível visualizar os matches de outros usuários- Entre as informações visíveis estavam imaginaryName, idade, fotos, gênero, sexuality, status e data de nascimento
Acesso sem autenticação a anexos de chat
- Os anexos compartilhados em chats se dividem em fotos e vídeos
- As fotos podem ser fotos comuns visualizáveis ou fotos com limite de 5 a 15 segundos
- Os vídeos podem ser vídeos comuns visualizáveis ou vídeos de reprodução única
- Fotos comuns são enviadas do app Feeld para
api.cloudinary.com, e a resposta retorna um photo_id- Depois, a foto é copiada para
feeld.coe entregue a usuários autenticados - São usados caminhos no formato
cdn/chat-attachment/<receiver_profileId>/<photo_id>ou<sender_profileId>/<photo_id> - Mesmo reduzindo a parte profileId do caminho para uma string arbitrária de pelo menos 1 caractere, a foto ainda era retornada para usuários autenticados
- Um caminho com
/v1/na frente retornava a URL da foto original armazenada no Cloudinary, e essa URL era acessível sem autenticação
- Depois, a foto é copiada para
- Fotos com tempo limitado usam parâmetros adicionais no upload, como
visibilityMilliseconds:15000- O endpoint do destinatário apaga a foto após 5 a 15 segundos do acesso, deixando-a indisponível
- O endpoint usando o profileId de quem enviou continuava retornando a foto para usuários autenticados mesmo após 5 a 15 segundos
- O caminho
/v1/retornava uma URL do Cloudinary, e essa URL era acessível sem autenticação
- Tanto vídeos comuns quanto vídeos de reprodução única incluíam a URL na mensagem de chat
- Vídeos comuns eram enviados para
us-east.stream-io-cdn.com - Vídeos de reprodução única usavam um fluxo de upload via
chat.stream-io-api.com - Ao obter a URL pela vulnerabilidade anterior de leitura de mensagens e trocar
u0026por&, um invasor podia assistir sem autenticação
- Vídeos comuns eram enviados para
- Vídeos de reprodução única podiam ser reproduzidos novamente pelo invasor, enquanto no app do destinatário apareciam como
video expiredapós uma visualização
Manipulação de mensagens, alteração de perfil e falsificação de likes
- No endpoint
chat.stream-io-api.com/messages/<messageId>, era possível operar sobre mensagens de terceiros com os métodos DELETE e PUT - Mensagens excluídas apareciam no chat como
This message was deleted, mas, se o invasor chamasse a mesma requisição DELETE, recebia de volta a mensagem original - O invasor podia modificar mensagens usando o messageId mesmo sem participar do chat
- Quando a vítima tocava na notificação, via a mensagem alterada
- Havia um indicador
editedabaixo da mensagem, mas sem mostrar quem a editou - O nome da conta não é único e pode ser alterado
- Ao trocar o parâmetro vulnerável
idda requisição GraphQLProfileUpdatepelo ID da vítima, era possível atualizar informações de perfil como nome, sexuality, idade e bio - Na requisição GraphQL
ProfileLike, era possível, estando logado como profile#1, fazer parecer que profile#2 enviou um “Like” para profile#3- No exemplo, após enviar um Like ao próprio perfil a partir de um perfil arbitrário, esse Like apareceu na lista Likes de uma conta premium
Envio de mensagens em chats de terceiros
- O invasor podia enviar mensagens em chats de outras pessoas mesmo sem participar deles
- O valor necessário era o channelID obtido na vulnerabilidade anterior de leitura de mensagens
- Ao enviar uma requisição POST para
channels/messaging/<channelID>/message, a mensagem era adicionada ao canal - A vítima recebia uma notificação e podia verificar a mensagem
- O sistema mostrava a notificação como vinda do nome do invasor, mas o invasor podia alterar o nome do perfil, e nomes não são únicos
Linha do tempo da divulgação
- Em 8 de março de 2024, a FORTBRIDGE divulgou todos os problemas à Feeld
- No mesmo dia, a Feeld pediu as informações das contas usadas nos testes
- Em 2 de abril de 2024, a FORTBRIDGE pediu uma atualização, e a Feeld solicitou adiar a publicação dizendo que estava investigando
- Em 28 de maio de 2024, a Feeld informou ter implantado várias correções e pediu até 2 semanas de atraso para verificar se os problemas encontrados haviam sido resolvidos
- Em 8 de junho de 2024, haviam se passado 3 meses desde o email inicial de divulgação
- Em 15 de julho de 2024, a Feeld respondeu que alguns problemas exigiam correções mais complexas
- Em 4 de agosto de 2024, a Feeld pediu que a publicação fosse adiada até a resolução dos itens restantes
- Em 16 de agosto de 2024, a Feeld respondeu que havia implementado mudanças para mitigar os achados restantes
- Em 8 de setembro de 2024, haviam se passado 6 meses desde a divulgação inicial
- Em 10 de setembro de 2024, o blog foi publicado
- Em agosto de 2025, a pesquisa foi apresentada na DEF CON 33
1 comentários
Comentários do Hacker News
Parece que a verificação de permissões foi implementada apenas no frontend, e não só em um ou dois endpoints, mas praticamente em tudo
Conceitualmente é um erro fácil de evitar, mas já vi erros parecidos com uma frequência que dá até vergonha de admitir
A solução “verifique todas as permissões no backend” me parece semelhante ao “coloque verificações de limites em todos os lugares” para buffer overflow. A comunidade como um todo sabe o que deve fazer, mas não é fácil fazer todo mundo aplicar isso de forma consistente
Já a verificação de permissões acontece em uma fronteira específica e tem a ver com a forma como a aplicação é projetada. Sempre que pude influenciar o modo de desenvolvimento de um projeto, insisti em separar claramente o desenvolvimento da API de backend do código do cliente frontend. Pela minha experiência, isso torna muito mais fácil evitar e testar esse tipo de problema, e ainda cria uma API para desenvolvedores “de graça”. Sinceramente, esse é o principal motivo pelo qual prefiro essa abordagem
Descobri porque o dono da conta lamp entrou em contato dizendo que todos os seus dados tinham sumido de repente. Olhando os logs, o Google Bot tinha clicado em todos os links “Delete” da tela interna de administração. Isso foi possível porque JavaScript é opt-in. Liguei para o desenvolvedor e expliquei o que ele tinha feito; naquele dia perdi muita confiança no pessoal de web
Sempre marco isso quando vejo, mas é bem preocupante como às vezes se pensa pouco no escopo da API do cliente
Dá vontade de culpar juniores, no-code e código feito por IA, mas eu sou tão preguiçoso quanto eles, então só balanço a cabeça e sigo em frente
É um ótimo motivo para não colocar informações pessoais exatas. Por exemplo, coisas como data de nascimento
Apps de namoro, em especial, parecem exigir esse tipo de informação, mas é melhor não fornecer. É melhor colocar uma data cerca de um ano para mais ou para menos em relação ao aniversário real
Este app de namoro não é muito conhecido, mas é voltado a pessoas com preferências diferentes, como BDSM e sexo em grupo, e a usuários queer. Em muitas regiões do mundo, esse tipo de informação é, nem é preciso dizer, extremamente sensível
Nesta semana apareceu bastante na imprensa porque está ganhando muito dinheiro
https://www.theguardian.com/technology/article/2024/sep/08/t...
Considerando a categoria do app, é uma falha no nível de negligência criminal
Acho que ameaça de prisão nos EUA e na UE, seguro relacionado a dados e o custo desse seguro serão os únicos freios. Se as fotos não forem do tipo que você colocaria no LinkedIn, o preço deveria ser absurdo
Claro que os incentivos não devem encorajar acobertamento
O setor de namoro online é uma bagunça. Só existem 2 ou 3 empresas com serviços que dá para chamar de úteis, e elas são malignas, incompetentes ou as duas coisas
Talvez agora precisemos de algo como um serviço de namoro federado open source. No mínimo, algo que não venda dados, não vaze fotos nuas e não faça as pessoas serem espancadas, estupradas ou assassinadas. Não que isso seja fácil
O ActivityPub também já tem uma estrutura que possibilitaria isso por meio da publicação de registros Person. Há um espaço enorme para inovação, especialmente se as necessidades de não monogamia, não heterossexualidade e não conformidade de gênero forem priorizadas
Mas apps de namoro são uma área realmente difícil de entrar. Para serem úteis, precisam de uma massa acumulada de usuários em uma região específica, e, quando se monetiza, o app inevitavelmente fica menos útil. Há um motivo para o okcupid ter piorado depois que deixou de ter um caráter sem fins lucrativos
E ainda há o problema de moderação
Se alguém quiser transformar a forma analógica em uma cópia digital, é direito da pessoa, mas ela precisa saber que nenhum sistema é, nem poderá ser, seguro o suficiente para impedir vazamentos e distribuição
Especialmente pessoas jovens não levam em conta as consequências e a vergonha que podem surgir, e provavelmente surgirão, no longo prazo. Oferecer esse tipo de recurso é apenas convidar resultados negativos
É realmente horrível. Fica claro que não houve nenhuma reflexão sobre segurança
Sou desenvolvedor de jogos, e nós nos esforçamos mais para manter os jogos justos do que essa empresa para manter os usuários seguros. Eles deveriam ser destruídos por processos
Mesmo antes de perceber que o app era cheio de bugs, fiquei muito surpreso ao ver que a seção de interesses não dava contexto nenhum. Por exemplo, quase todo mundo tinha Domination ou Submission como interesse, mas não havia nenhum contexto sobre qual papel a pessoa queria. Não saber o quanto isso é fundamentalmente errado nessa cena significa não saber nada no geral
Mensagens e fotos privadas são outra questão
Para ser provocador, isso é um problema do GraphQL
O GraphQL permite que o frontend consulte dados. É legal, mas, do ponto de vista do backend, isso é muito opaco e geralmente é implementado com bibliotecas de terceiros que não sabem nada sobre controle de acesso
Se você não vai implementar o controle de acesso no próprio banco de dados, fica muito difícil, no código do backend, destrinchar consultas GraphQL e descobrir quais registros devem ser retornados ou restringidos. Fazer isso no banco de dados não é a pior opção e certamente é melhor do que fazer no frontend
Para implementar um controle de acesso adequado no backend, é preciso entender a consulta, conhecer o esquema do banco de dados e criar modelos, classes, funções etc. capazes de decidir se “o user_id XXX pode ou não ver esta imagem neste contexto”. No GraphQL, é muito mais fácil implementar isso no frontend, então é evidente que foi o que fizeram
Não estou dizendo que a implementação do GraphQL tenha sido boa, nem que a culpa seja inteiramente do GraphQL. Quero dizer que o GraphQL tenta eliminar a necessidade de o backend entender a consulta, o que torna situações de segurança complexas como essa mais difíceis e, portanto, torna esse erro mais fácil de cometer
[0] Por exemplo, uma determinada imagem pode ter acesso público no perfil do usuário, mas ser visível apenas para pessoas com match, ou apenas no contexto de um chat (excluindo chats em grupo), ou nunca ser acessível a um usuário bloqueado. Só esse caso já pode criar um monte de casos de borda complexos
Não é preciso mexer na AST nem entender o contexto do restante da consulta. No resolver que busca fotos, basta responder: “o usuário ABC pode ver as fotos do usuário XYZ?”. Se for ineficiente, dá para pré-carregar alguns dados ou usar um dataloader
Mas, se você estiver usando alguma biblioteca mágica que transforma GraphQL em SQL, aí a história muda
https://hasura.io/docs/2.0/security/allow-list/
Uma ideia simples é implementar a autorização no modelo de dados. Ou seja, fazer o GraphQL delegar
getelista um modelo de recursos que possa implementar autorização conforme o contexto da requisição[1] https://www.apollographql.com/docs/apollo-server/security/au...
[2] https://docs.graphene-python.org/projects/django/en/latest/a...
Foi uma divulgação surpreendentemente responsável e cuidadosa
Não é uma grande surpresa. Eu uso, mas diria que foi feito com tanta incompetência quanto o app do meu banco. Talvez até pior; quase nada funciona direito
Não sei como conseguiram fazer isso
Vendo este app e o Fetlife, há um grande problema nessas comunidades continuarem presas ao primeiro app que apareceu, independentemente da qualidade
Então, há algum tempo, fizeram um flag day em que lançaram ao mesmo tempo um novo app e novos servidores para todo mundo, e a maioria sequer conseguiu fazer login. Quem conseguiu entrar e era cliente pagante perdeu os benefícios premium, likes e chats desapareceram, e assim por diante. Eu acabei nunca conseguindo fazer login e abandonei o app naquele ponto
Sinceramente, me surpreende que os pesquisadores tenham esperado tanto para divulgar
Se você dá 6 meses para uma startup ruim corrigir uma brecha de privacidade tão grave, ela continuará abusando do privilégio de coletar esse tipo de informação em primeiro lugar. Acho que deveriam dar só 2 meses e divulgar. Eles precisam aprender que não se brinca de roleta com as informações privadas das pessoas
Ex.: https://news.ycombinator.com/item?id=41517747