Skip to content
This page has been auto-translated and may contain errors.View in English

Triagem de bugs

Uma mensagem chega no chat da equipe:

Acho que os clientes conseguem ver os pedidos uns dos outros.

Isso pode ser urgente. Pode ser um mal-entendido. Ninguém sabe ainda.

Triagem de bugs é o trabalho que acontece entre "alguém encontrou algo" e "a equipe sabe o que fazer a seguir".

Para bugs de segurança, a triagem responde a quatro perguntas:

  1. Conseguimos reproduzir?
  2. Que dano isso causa?
  3. Quão difícil é explorar?
  4. O que acontece depois?

Reproduza o bug

Uma descoberta de segurança é só uma alegação até que alguém consiga fazer acontecer de propósito.

Comece pela rota vulnerável:

Vulnerable
js
app.get('/orders/:id', async (req, res) => {
  const order = await db.orders.findById(req.params.id)
  res.json(order)
})

Agora escreva a reprodução:

bash
# Autenticado como cliente 4102
curl -i https://app.example.com/orders/88213 \
  -H "Cookie: sid=<sessão do cliente 4102>"

# Esperado: 403 ou 404
# Real: 200, com o pedido de outro cliente

Uma boa reprodução inclui a conta, a requisição, o resultado esperado e o resultado real.

"Muda o id e quebra" é uma anotação. O bloco acima é algo que outro desenvolvedor consegue executar.

Só teste onde você tem permissão

Execute reproduções contra sistemas que você possui, mantém ou tem permissão por escrito para testar.

A mesma requisição contra o app de outra pessoa não é triagem.

JunoReproduza o bug Uma reprodução é a receita para fazer o bug acontecer de novo.

Escreva a conta, a requisição, o que deveria ter acontecido e o que realmente aconteceu. Se outro desenvolvedor conseguir seguir os passos sem precisar perguntar nada, você tem um relato útil.

JunoReproduza o bug Reduza o bug à menor requisição que ainda prove que ele existe. Remova cabeçalhos, passos e dados extras até que tirar qualquer outra coisa faça o comportamento desaparecer.

Essa requisição menor é mais fácil de testar, mais fácil de corrigir e mais difícil de descartar. Ela também mostra qual parte do sistema está de fato tomando a decisão insegura.

JunoReproduza o bug Reproduza primeiro quando o bug é apenas suspeito. Contenha primeiro quando o bug já está sendo explorado.

Se os logs mostram contas desconhecidas percorrendo ids de pedidos, ou clientes relatam ver dados que nunca foram deles, preserve as evidências e restrinja o acesso antes de mais nada.

Não gaste a melhor hora do incidente provando o bug para si mesmo. A reprodução pode esperar o tempo suficiente para que os logs não evaporem, e logs adoram evaporar. É um dos hobbies menos charmosos deles.

Explique o impacto

Impacto é o que a pessoa que explora o bug acaba conseguindo obter ou fazer.

Para o bug do pedido, o impacto poderia ser:

Qualquer cliente autenticado consegue ler o endereço de entrega e o histórico de pedidos de outro cliente.

Essa frase é melhor do que:

IDOR em /orders/:id.

IDOR é o nome técnico de segurança: insecure direct object reference (referência direta insegura a objeto). Significa que uma requisição inclui o id de um objeto, e o servidor não verifica se quem fez a chamada tem permissão para usar aquele objeto.

O nome ajuda engenheiros a classificar o bug. O impacto ajuda todo mundo a entender por que ele importa.

Pergunte:

  • Que dado ou ação fica exposto?
  • De quem é esse dado ou ação?
  • Quantas pessoas podem ser afetadas?
  • O que isso pode viabilizar em seguida?

A severidade começa pelo dano causado, não pela esperteza do bug.

JunoExplique o impacto Impacto é o dano descrito em linguagem simples. O que a pessoa passa a poder ler, mudar ou fazer?

Os nomes técnicos de segurança são úteis, mas não bastam sozinhos. "Qualquer cliente consegue ler o endereço de outro cliente" comunica o que importa para mais gente do que "IDOR".

JunoExplique o impacto Quando você não sabe o número exato, dê uma estimativa. "Todos os pedidos desde março, cerca de 40.000" é melhor do que deixar o campo em branco.

Depois, siga o dano um passo adiante. Um endereço de e-mail exposto pode facilitar phishing. Um token de redefinição de senha exposto pode virar sequestro de conta. Um passo mantém o relato ancorado na realidade; cinco passos transformam tudo em ficção de fã com etiqueta de severidade.

JunoExplique o impacto Alguns achados não têm impacto alcançável hoje, e forçar um encaixe no relatório só piora a fila inteira. Uma flag de cookie ausente em uma rota que não aceita escrita entre sites é uma lacuna real, mas não é uma história de sequestro de conta ativa.

Registre como "sem impacto alcançável hoje" e escreva o que precisaria mudar para que isso importasse. Essa condição futura é a parte útil. É a pista que alguém vai precisar quando a rota mudar seis meses depois e aquele achado antigo de baixo risco deixar de repente de ser tão decorativo.

Avalie a explorabilidade

O impacto mostra o que o bug pode causar. A explorabilidade mostra o que é preciso para causá-lo.

Para o bug do pedido, o atacante precisa de:

  • uma conta normal de cliente
  • um id de pedido válido ou adivinhável
  • uma requisição

Isso é altamente alcançável. Um bug que exige uma conta de administrador, acesso físico, timing perfeito e um clique da vítima é diferente.

Anote as pré-condições. Elas mantêm a conversa sobre severidade concreta.

Pré-condiçõesO que significa
AnônimoQualquer pessoa na internet pode tentar.
AutenticadoÉ preciso uma conta normal.
Papel privilegiadoÉ preciso um papel de equipe ou administrador.
Ação da vítimaOutra pessoa precisa clicar ou enviar algo.
Id adivinhávelO alvo pode ser encontrado tentando valores.
Janela de tempoO bug só funciona durante um momento estreito.
JunoAvalie a explorabilidade Explorabilidade é a lista de condições que precisam ser verdadeiras para o bug funcionar.

Anônimo é mais fácil de alcançar do que autenticado. Autenticado é mais fácil de alcançar do que administrador. Uma requisição é mais fácil de alcançar do que uma cadeia que depende de timing e de outra pessoa clicando em algo.

JunoAvalie a explorabilidade Trate as pré-condições como afirmações a verificar. Se o relato diz "requer administrador", confira quantos administradores existem e como esse papel é concedido.

Se diz que os ids não são adivinháveis, olhe os ids reais. Um timestamp com um contador no final não deixa de ser adivinhável só porque a variável tem um nome bonito. Nomes já traíram gente melhor do que nós.

JunoAvalie a explorabilidade A explorabilidade muda quando o sistema ao redor muda. Um achado classificado como baixo risco por exigir uma conta de equipe vira um achado diferente quando a ferramenta de suporte se abre para parceiros.

Coloque a data e o motivo ao lado da classificação. O seu eu futuro, ou a pessoa que herdar seu serviço depois de três reorganizações, precisa saber qual suposição sustentava aquela classificação. Suposições envelhecem como leite em uma sala de servidores.

Decida o que acontece a seguir

Agora combine as peças:

CampoExemplo
ReproduçãoO cliente 4102 solicita o pedido 88213 e recebe o pedido de outro cliente.
ImpactoQualquer cliente autenticado consegue ler o endereço de entrega e o histórico de pedidos de outro cliente.
ExplorabilidadeConta normal, id adivinhável, uma requisição.
Categoria OWASPA01 Controle de Acesso Quebrado.
Próxima açãoCorrigir antes do lançamento, adicionar teste de propriedade, verificar rotas semelhantes.

A categoria OWASP ajuda a classificar o bug. A próxima ação transforma o relato em trabalho.

Para esse bug, a correção é verificar a propriedade antes de retornar o pedido:

Fixed
js
app.get('/orders/:id', async (req, res) => {
  const order = await db.orders.findById(req.params.id)

  if (!order || order.userId !== req.user.id) {
    return res.status(404).json({ error: 'Order not found' })
  }

  res.json(order)
})

O teste correspondente deve provar o caminho ruim, não só o caminho feliz.

Uma boa anotação de triagem gera trabalho de acompanhamento

Corrija esse endpoint.

Adicione um teste de regressão.

Procure o mesmo padrão em rotas próximas.

JunoDecida o que acontece a seguir A triagem termina com uma decisão. Corrigir agora, corrigir depois com um motivo, ou aceitar o risco de propósito.

Para esse bug do pedido, a decisão deve ser direta: corrigir antes do lançamento, adicionar um teste e verificar rotas próximas em busca da mesma falta de verificação de propriedade.

JunoDecida o que acontece a seguir O acompanhamento importa tanto quanto a correção. Um bug de controle de acesso quebrado em uma rota é uma dica para pesquisar o resto do recurso.

Procure handlers que leem req.params.id e retornam ou alteram dados sem comparar propriedade, papel ou inquilino (tenant). Essa busca transforma um relato em uma correção de classe inteira.

JunoDecida o que acontece a seguir A correção tem três camadas: consertar a rota, travar o bug com um teste de regressão e procurar o mesmo padrão em outros lugares. Pule a terceira e você vai acabar redescobrindo o mesmo bug com uma URL diferente e uma sensação renovada de decepção.

Tenha cuidado também com as escolhas de resposta. Retornar 404 tanto para pedidos inexistentes quanto para não autorizados esconde se o id existe. Retornar 403 pode ser mais claro para os clientes, mas pode vazar que o registro é real. Escolha o comportamento deliberadamente e mantenha a consistência por recurso.

Experimente

Um relato de bug chega com uma única linha: "O link de redefinição de senha continua funcionando depois que você já o usou."

Faça a triagem. Preencha os cinco campos: reprodução, impacto, explorabilidade, categoria OWASP, próxima ação.

Compare suas respostas
CampoUma resposta razoável
ReproduçãoSolicite uma redefinição, siga o link, defina uma senha, depois abra o mesmo link de novo. Ele ainda é aceito e permite definir outra senha.
ImpactoQualquer pessoa que veja esse link, uma única vez, pode sequestrar a conta mais tarde. Links de redefinição ficam em caixas de entrada, histórico do navegador e e-mails encaminhados por muito tempo.
ExplorabilidadeNão é preciso conta nem adivinhação. É preciso ter acesso ao link, então a dificuldade depende inteiramente de para onde esse link viajou.
Categoria OWASPA07 Falhas de Autenticação. O app não consegue estabelecer com confiabilidade quem é a pessoa quando uma credencial já usada continua funcionando.
Próxima açãoInvalidar o token no primeiro uso e adicionar uma expiração, se ainda não houver uma. Depois, verificar se links de verificação de e-mail e de convite têm a mesma falha.

Duas coisas valem a pena destacar.

O impacto não é "um token não é invalidado", é "alguém pode sequestrar a conta". Um campo que só repete o bug não acrescenta nada; o campo existe para dizer quem sai machucado.

E a próxima ação termina olhando para os lados. Tokens de uso único costumam ser gerados por código compartilhado, então um bug em um fluxo muito frequentemente está presente nos outros também. Uma triagem que corrige exatamente uma rota e para por aí fez só metade do trabalho.

Para onde isso vai a seguir

A triagem de bugs encerra a primeira seção. Agora você consegue perguntar o que pode dar errado, nomear bugs comuns em produção e transformar um achado em trabalho.

A seguir, o manual entra em segurança de entrada e de dados, começando por nunca confie na entrada do usuário.