Principiante
CVSS 5.3 - Medio
15 min lectura
Insecure Direct Object References (IDOR)
Acceso no autorizado a objetos modificando parámetros de referencia
¿Qué es IDOR?
IDOR ocurre cuando una aplicación expone una referencia a un objeto interno (archivo, directorio, registro de base de datos) y no verifica adecuadamente si el usuario tiene permiso para acceder a ese objeto.
Ejemplo Clásico
// Usuario autenticado como ID=123
GET /api/users/123/profile → ✅ Su propio perfil
// Cambiar ID en la URL
GET /api/users/124/profile → ❌ Debería rechazar
GET /api/users/125/profile → ❌ Perfil de otro usuario
// Código vulnerable
app.get('/api/users/:id/profile', authenticate, async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(user); // ¡Sin verificar si req.user.id === req.params.id!
});Ejemplos Reales
1. Documentos/Archivos
GET /api/documents/456/download
GET /invoices/invoice_123.pdf
GET /files?id=789
// Atacante prueba IDs secuenciales
for (let i = 1; i <= 1000; i++) {
fetch(`/api/documents/${i}/download`);
}2. Modificación de Datos
PUT /api/orders/456
{
"status": "shipped",
"address": "attacker address"
}
DELETE /api/posts/789
POST /api/admin/users/123/promote3. APIs con UUIDs (menos obvios)
// UUIDs son más seguros pero NO suficientes
GET /api/files/a1b2c3d4-e5f6-7890-abcd-ef1234567890
// Si se exponen en respuestas, pueden ser enumerados
GET /api/users/me/documents
{
"documents": [
{"id": "uuid-123", "name": "doc1.pdf"},
{"id": "uuid-456", "name": "doc2.pdf"}
]
}
// Atacante puede intentar acceder a uuid-456 de otro usuarioMitigación
1. Verificar Autorización
// ✅ SEGURO
app.get('/api/users/:id/profile', authenticate, async (req, res) => {
// Verificar que el usuario solo acceda a su propio perfil
if (req.user.id !== parseInt(req.params.id) && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
const user = await db.users.findById(req.params.id);
res.json(user);
});
// Para documentos
app.get('/api/documents/:id', authenticate, async (req, res) => {
const doc = await db.documents.findById(req.params.id);
if (!doc) {
return res.status(404).json({ error: 'Not found' });
}
// Verificar ownership
if (doc.userId !== req.user.id) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(doc);
});2. Usar Referencias Indirectas
// En lugar de exponer IDs directos, usar mapeo
// GET /api/documents/doc_abc123 (token de sesión)
const session = {
userId: 123,
documentMap: {
'doc_abc123': 456, // ID real en DB
'doc_xyz789': 457
}
};
app.get('/api/documents/:token', (req, res) => {
const realId = req.session.documentMap[req.params.token];
if (!realId) {
return res.status(404).json({ error: 'Not found' });
}
const doc = await db.documents.findById(realId);
res.json(doc);
});3. Filtrar por Usuario en Queries
// ✅ Siempre incluir userId en la query
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await db.orders.findOne({
where: {
id: req.params.id,
userId: req.user.id // ¡Filtrar por usuario!
}
});
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}
res.json(order);
});
// Con Prisma
const order = await prisma.order.findFirst({
where: {
id: parseInt(req.params.id),
userId: req.user.id
}
});4. UUIDs + Autorización
// UUIDs reducen enumeración pero NO son suficientes
import { v4 as uuidv4 } from 'uuid';
// Usar UUID como ID
const doc = await db.documents.create({
id: uuidv4(), // "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
userId: req.user.id,
content: req.body.content
});
// ¡Aún así verificar autorización!
app.get('/api/documents/:uuid', authenticate, async (req, res) => {
const doc = await db.documents.findOne({
where: {
id: req.params.uuid,
userId: req.user.id // ¡Verificar ownership!
}
});
if (!doc) return res.status(404).json({ error: 'Not found' });
res.json(doc);
});Checklist de Seguridad
- ✓ Verificar autorización en CADA endpoint que accede a recursos
- ✓ Nunca confiar en parámetros del cliente (IDs, UUIDs, etc)
- ✓ Usar filtros WHERE con userId en todas las queries
- ✓ Implementar RBAC (Role-Based Access Control) para recursos compartidos
- ✓ Logging de intentos de acceso no autorizados
- ✓ Usar UUIDs en lugar de IDs secuenciales (reduce enumeración)
- ✓ Testing: Intentar acceder a recursos de otros usuarios en QA