DEV Community

Cover image for Entendendo IDOR na prática com um laboratório em Node.js
Emerson Vieira
Emerson Vieira

Posted on

Entendendo IDOR na prática com um laboratório em Node.js

IDOR, ou Insecure Direct Object Reference, é uma vulnerabilidade de controle de acesso que acontece quando uma aplicação utiliza uma referência direta a um objeto interno — como um usuário, documento, pedido ou fatura — sem verificar se o usuário autenticado tem permissão para acessar aquele recurso.

Considere este endpoint:

GET /api/documents/101
Enter fullscreen mode Exit fullscreen mode

Se o usuário conseguir alterar 101 para 102 e acessar o documento de outra pessoa, temos um IDOR:

GET /api/documents/102
Enter fullscreen mode Exit fullscreen mode

O problema não é o ID estar na URL. O problema é a API confiar nesse ID sem validar se o usuário pode acessar o objeto solicitado.

O projeto Security Labs

Para demonstrar essa vulnerabilidade na prática, criei o projeto open source Security Labs, um ambiente em Node.js com Express para estudar falhas comuns de segurança por meio de laboratórios interativos.

Repositório:

github.com/mensonones/security

Cada laboratório apresenta dois cenários:

Modo Comportamento
Vulnerável Executa a ação sem aplicar a proteção necessária
Seguro Implementa a validação ou autorização adequada

O primeiro laboratório do projeto demonstra uma falha de IDOR.

Cenário do laboratório

A aplicação simula dois usuários:

Usuário ID
Alice 1
Bob 2

Também existem dois documentos:

Documento Proprietário
101 Alice
102 Bob

No modo vulnerável, Alice consegue acessar o documento de Bob alterando apenas o ID da requisição:

GET /api/idor/vulnerable/documents/102
Enter fullscreen mode Exit fullscreen mode

No modo seguro, a API verifica o proprietário do documento e bloqueia a operação:

GET /api/idor/secure/documents/102
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "Acesso Negado: Você não tem autorização para visualizar este documento."
}
Enter fullscreen mode Exit fullscreen mode

Essa comparação demonstra uma diferença importante:

Autenticação identifica quem é o usuário. Autorização define quais recursos e ações estão disponíveis para ele.

Endpoint vulnerável

A versão vulnerável autentica o usuário, mas não realiza autorização em nível de objeto:

router.get('/vulnerable/documents/:id', checkAuth, (req, res) => {
  const docId = parseDocumentId(req.params.id);

  if (docId === null) {
    return res.status(400).json({
      error: 'ID de documento inválido.'
    });
  }

  const doc = documents[docId];

  if (!doc) {
    return res.status(404).json({
      error: 'Documento não encontrado.'
    });
  }

  return res.json({
    _warning:
      'ENDPOINT VULNERÁVEL: Nenhuma validação de propriedade realizada.',
    document: doc
  });
});
Enter fullscreen mode Exit fullscreen mode

O middleware checkAuth confirma quem está fazendo a requisição, mas o endpoint retorna o documento sem verificar se ele pertence ao usuário autenticado.

Nesse cenário, conhecer ou descobrir o identificador de outro documento é suficiente para acessá-lo.

Endpoint seguro

A versão corrigida adiciona a validação de propriedade:

router.get('/secure/documents/:id', checkAuth, (req, res) => {
  const docId = parseDocumentId(req.params.id);

  if (docId === null) {
    return res.status(400).json({
      error: 'ID de documento inválido.'
    });
  }

  const doc = documents[docId];

  if (!doc) {
    return res.status(404).json({
      error: 'Documento não encontrado.'
    });
  }

  if (doc.ownerId !== req.userId) {
    return res.status(403).json({
      error:
        'Acesso Negado: Você não tem autorização para visualizar este documento.'
    });
  }

  return res.json({
    message: 'Acesso autorizado.',
    document: doc
  });
});
Enter fullscreen mode Exit fullscreen mode

A parte essencial da correção é esta:

if (doc.ownerId !== req.userId) {
  return res.status(403).json({
    error:
      'Acesso Negado: Você não tem autorização para visualizar este documento.'
  });
}
Enter fullscreen mode Exit fullscreen mode

Mesmo que o usuário conheça o ID de outro documento, o backend impede o acesso indevido porque compara o proprietário do recurso com o usuário autenticado.

Validação de entrada

O laboratório também valida o formato do identificador recebido.

Evite depender apenas de:

parseInt(req.params.id, 10);
Enter fullscreen mode Exit fullscreen mode

Isso porque entradas como 101abc podem ser convertidas para 101:

parseInt('101abc', 10);
// 101
Enter fullscreen mode Exit fullscreen mode

Uma validação mais estrita pode ser feita assim:

function parseDocumentId(id) {
  if (!/^\d+$/.test(id)) {
    return null;
  }

  return Number(id);
}
Enter fullscreen mode Exit fullscreen mode

Dessa forma, identificadores malformados são rejeitados com uma resposta 400 Bad Request.

Essa validação, entretanto, não corrige o IDOR. A correção da vulnerabilidade continua sendo a autorização em nível de objeto.

Como executar

Clone o repositório:

git clone https://github.com/mensonones/security.git
cd security
Enter fullscreen mode Exit fullscreen mode

Instale as dependências e inicie o servidor:

npm install
npm start
Enter fullscreen mode Exit fullscreen mode

Acesse o painel principal:

http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Ou abra diretamente o laboratório de IDOR:

http://localhost:3000/labs/idor/
Enter fullscreen mode Exit fullscreen mode

Como prevenir IDOR

Verifique o acesso ao objeto

Sempre confirme se o usuário autenticado pode acessar o recurso solicitado:

if (resource.ownerId !== req.userId) {
  return res.status(403).json({
    error: 'Acesso negado.'
  });
}
Enter fullscreen mode Exit fullscreen mode

A validação deve acontecer no backend, mesmo que a interface esconda botões, páginas ou funcionalidades de determinados usuários.

Restrinja a consulta desde a origem

Quando possível, inclua o usuário autenticado na própria consulta:

const document = await database.documents.findFirst({
  where: {
    id: documentId,
    ownerId: req.userId
  }
});
Enter fullscreen mode Exit fullscreen mode

Isso reduz o risco de buscar o recurso e esquecer de validar sua propriedade posteriormente.

Não confie em IDs enviados pelo cliente

IDs presentes na URL, query string, headers ou corpo da requisição são dados controlados pelo usuário:

GET /api/orders/5001
Enter fullscreen mode Exit fullscreen mode
{
  "documentId": 101
}
Enter fullscreen mode Exit fullscreen mode

O backend deve aplicar a autorização independentemente de onde o identificador foi recebido.

Não use UUID como substituto de autorização

UUIDs dificultam a enumeração, mas não impedem IDOR:

GET /api/documents/49fd2c62-9e20-4ce8-9373-e87bb72f0196
Enter fullscreen mode Exit fullscreen mode

Se a API não verificar a permissão do usuário, o endpoint continua vulnerável.

Identificadores difíceis de descobrir são apenas uma camada complementar de proteção.

Conclusão

IDOR é uma vulnerabilidade simples, mas pode permitir acesso a documentos privados, dados pessoais, pedidos, faturas e outros recursos sensíveis.

O erro normalmente nasce desta suposição:

Se o usuário está autenticado, ele pode acessar o recurso.

A pergunta correta é:

Este usuário autenticado pode executar esta ação sobre este objeto específico?

O laboratório de IDOR do projeto Security Labs demonstra essa diferença de forma prática, comparando um endpoint vulnerável com uma implementação segura.

O código completo está disponível em:

github.com/mensonones/security

Autenticação identifica o usuário. Autorização determina o que ele pode fazer.

Top comments (0)