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.
Top comments (0)