1

Um id na URL não prova que o dado é seu: o filtro que quase toda API esquece

Peguei uma API pra revisar e testei a coisa mais boba possível: troquei o id do meu recurso pelo id do vizinho, direto na URL.

GET /orders/10432   -> 200, o meu pedido
GET /orders/10431   -> 200, o pedido de OUTRA conta

Sem erro, sem 403. O dado veio inteiro: nome, valor, e-mail de contato.

O endpoint conferia autenticação (o token era válido) e parava aí. Nunca perguntava se aquele pedido pertencia a quem pediu. O código problemático costuma ser este:

// vulnerável: confia no id que veio da URL
app.get('/orders/:id', auth, async (req, res) => {
  const order = await db.order.findUnique({ where: { id: req.params.id } })
  res.json(order)
})

A correção é uma cláusula. O dono entra no WHERE, não numa checagem que alguém lembra de fazer depois:

// corrigido: dono no WHERE
app.get('/orders/:id', auth, async (req, res) => {
  const order = await db.order.findFirst({
    where: { id: req.params.id, userId: req.user.id },
  })
  if (!order) return res.sendStatus(404)
  res.json(order)
})

E o mais importante: um teste negativo, que tenta o acesso indevido e exige a porta fechada. Rode um por endpoint que recebe id:

test('não vaza pedido de outro dono', async () => {
  const res = await request(app)
    .get(`/orders/${otherUsersOrderId}`)
    .set(`Authorization`, `Bearer ${meuToken}`)
  expect(res.status).toBe(404) // ou 403
})

Isso é broken access control, o item nº 1 do OWASP Top 10 há anos. É a classe de bug mais barata de fechar e a mais fácil de subir pra produção sem perceber, porque num fluxo normal ninguém digita o id de outra pessoa — o problema só existe pra quem procura.

Versão completa no blog: https://revin.com.br/pt/blog/troquei-um-numero-na-url-e-vi-o-pedido-de-outro-cliente

Carregando publicação patrocinada...