Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

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 —

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

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

GET /api/documents/102

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

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

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

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

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
  });
});

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
  });
});

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.'
  });
}

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);

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

parseInt('101abc', 10);
// 101

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

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

  return Number(id);
}

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

Instale as dependências e inicie o servidor:

npm install
npm start

Acesse o painel principal:

http://localhost:3000

Ou abra diretamente o laboratório de IDOR:

http://localhost:3000/labs/idor/

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.'
  });
}

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
  }
});

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
{
  "documentId": 101
}

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

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.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.