cd ~/blog

~/vulns/idor-insecure-direct-object-reference.md

IDOR

web 24-10-2025
IDOR

~6 min de lectura


IDOR (Insecure Direct Object Reference) es una de las vulnerabilidades más comunes y peligrosas en aplicaciones web. Este artículo explica cómo funciona, cómo detectarla con Burp Suite o ZAP, ejemplos reales en Django y las buenas prácticas para prevenirla.

1. ¿Qué es IDOR?

IDOR (Insecure Direct Object Reference) es una vulnerabilidad de autorización que ocurre cuando una aplicación web expone directamente referencias a objetos internos —archivos, registros de base de datos, documentos o funcionalidades— sin validar adecuadamente si el usuario autenticado tiene permiso para acceder a ellos.

Esta vulnerabilidad permite a un atacante acceder, visualizar, modificar o eliminar datos sensibles simplemente manipulando valores en la URL, parámetros POST o cabeceras, como user_id=123 o invoice_id=999.

Un ataque IDOR exitoso puede comprometer:

  • Datos personales (emails, teléfonos, direcciones).
  • Información financiera (facturas, transacciones).
  • Credenciales almacenadas (a través de respuestas expuestas).
  • Funcionalidades administrativas restringidas.

IDOR se clasifica dentro de las fallas de acceso no autorizado, y es especialmente común en aplicaciones con APIs RESTful o interfaces de usuario dinámicas, donde el backend confía ciegamente en los parámetros proporcionados por el cliente.

IDOR forma parte del grupo de vulnerabilidades asociadas a una mala implementación de controles de acceso, junto con Broken Access Control.

2. ¿Cómo funciona un ataque IDOR?

El flujo de ataque es sencillo, pero devastador:

  1. El usuario autenticado (ej. Alice) accede a su recurso:
    GET /api/v1/users/1001/profile → devuelve sus datos.

  2. El atacante (Bob, con user_id=1002) intenta acceder a:
    GET /api/v1/users/1001/profile modificando manualmente el ID.

  3. Si el servidor no verifica que Bob tenga permiso para ver el perfil de Alice, devuelve la información de Alice.

  4. El atacante puede escalar: iterar IDs, acceder a endpoints administrativos, o modificar datos.

Condiciones comunes que permiten IDOR:

  • Uso de valores secuenciales, predecibles o guessables (1210001001).
  • Falta de validación en el lado del servidor (if user.role == 'admin' ausente o mal implementado).
  • Confianza en el frontend para ocultar funcionalidades (no es suficiente; el backend debe revalidar).
  • APIs que devuelven más datos de los necesarios (/user/123 devuelve email, password_hash, token, etc.).

Tipos comunes de IDOR:

  • User-Based IDOR: Un usuario accede a perfiles/órdenes de otros.
  • Admin IDOR: Acceso no autorizado a funcionalidades administrativas (/admin/delete?user_id=1001).
  • File/Resource IDOR: Acceso a documentos privados (/download?file_id=55).
  • Vertical vs Horizontal Privilege Escalation:
    • Horizontal: acceder a otro usuario del mismo nivel (Bob → Alice).
    • Vertical: elevar a nivel administrador (usuario → admin).

3. Herramientas

  1. Burp Suite (Pro / Community) – esencial para testear IDOR

    • Intercepta y modifica requests fácilmente.
    • Repeater: cambia user_id y observa respuestas.
    • Intruder: automatiza el fuzzing de IDs (1, 2, 3, ..., 1000).
    • Comparador de respuestas para detectar diferencias sutiles.
  2. ZAP (OWASP Zed Attack Proxy) – alternativa gratuita

    • Escaneo automático de endpoints con parámetros.
    • Fuzzing integrado para identificar respuestas inesperadas.
    • Ideal para pruebas iniciales en entornos restringidos.
  3. Autorización (extensión de Burp Suite)

    • Detecta automáticamente brechas en autorización.
    • Compara respuestas entre usuarios con distintos roles.
    • Muy útil para identificar casos donde un usuario normal accede a contenido de admin.
  4. SecLists – ids.txt

  5. Scripts personalizados (Python, Bash)

    • Automatización rápida para atacar APIs específicas.
    • Ideal para pruebas controladas o PoCs.
    • Ejemplo: uso de requests para iterar IDs con sesión autenticada.

4. Ejemplo conceptual

Supón que una app de facturación tiene este endpoint:

GET /api/invoices/555 HTTP/1.1
Host: billing.example.com
Authorization: Bearer user_session_xyz

La respuesta:

{
  "id": 555,
  "user_id": 2001,
  "amount": "1250.00",
  "date": "2025-03-15",
  "details": "Servicio de hosting anual"
}

Un atacante cambia 555 por 556557, etc. Si el servidor solo verifica autenticación, no autorización, puede obtener facturas de otros usuarios.

No hace falta SQLi, solo manipulación de referencias directas.


5. PoC

He desarrollado una aplicación Django vulnerable a IDOR para fines educativos. El código y pruebas están disponibles en: github.com/wh01s17/idor_vulnerable


git clone https://github.com/wh01s17/idor_vulnerable
cd idor_vulnerable

python -m venv .venv
source .venv/bin/activate

pip install -r requirements.txt
python manage.py migrate

python manage.py runserver 0.0.0.0:8000

La app tiene:

  • Dos usuarios: Alice (ID: 1) y Bob (ID: 2).
  • Cada uno con sus facturas (/invoices/).
  • Endpoint vulnerable: GET /invoices/<id>/

Escenario de ataque (Bob → Alice)

  1. Bob inicia sesión:

curl -c cookies.txt -X POST http://localhost:8000/login/ \
  -d "username=bob&password=bob123"
  • Consulta su factura:

  • curl -b cookies.txt http://localhost:8000/invoices/2/
    # Respuesta: {"id": 2, "user_id": 2, "amount": "350.00", ...}
  • Intenta acceder a la factura de Alice (ID 1):

  • curl -b cookies.txt http://localhost:8000/invoices/1/
    # ¡Éxito! Respuesta: {"id": 1, "user_id": 1, "amount": "1200.00", ...}
  • El backend no valida:

  • # 🚫 Vulnerable: no comprueba user_id
    invoice = get_object_or_404(Invoice, id=invoice_id)
  • Solución correcta:

  • # ✅ Segura: vincula al usuario autenticado
    invoice = get_object_or_404(Invoice, id=invoice_id, user=request.user)

    6. IDOR vs Broken Access Control

    AspectoIDOR (Insecure Direct Object Reference)Broken Access Control (BAC)
    DefiniciónAcceso a objetos vía manipulación directa de IDsFalta de validación de permisos en cualquier operación
    Nivel de granularidadMás específico: ataca objetos individuales (facturas, perfiles)Más amplio: incluye IDOR, funcionalidades, rutas, APIs
    Vector principalParámetros en URL, body o headersCualquier punto donde se salta autorización (roles, scopes)
    Puede incluirCambio de user_id=123 en una peticiónAcceso a /admin siendo usuario normal
    Ejemplo típicoGET /user/123 → ver perfil ajenoGET /api/deleteAllUsers → sin permisos
    En OWASP Top 10Históricamente parte de BACCategoría A01 en OWASP Top 10 2021 y 2023
    ¿Es IDOR un BAC?Sí, es un subtipo común de Broken Access ControlNo, BAC es la categoría general

    💡 Conclusión: IDOR es una manifestación específica de Broken Access Control. Hoy se reporta bajo A01 en OWASP, pero sigue siendo un nombre clave en pentesting.


    7. Patrones de logs y señales

    • Múltiples requests al mismo endpoint (/user/X/invoice/X) desde la misma IP.
    • Series de códigos 200 OK para IDs distintos en rápida sucesión (fuzzing).
    • Respuestas válidas para IDs no pertenecientes al usuario autenticado.
    • Códigos 403 Forbidden solo cuando se accede a IDs muy altos o extremos (indicando intentos bloqueados).
    • Logs de aplicación mostrando acceso a user_id=100 siendo usuario 101.
    • Actividad fuera del rango normal (ej: usuario normal accede a ?admin=true).

    Ejemplo de línea de access_log (Django):

    "GET /invoices/101/ HTTP/1.1" 200 821 "user=bob"
    "GET /invoices/102/ HTTP/1.1" 200 902 "user=bob"
    "GET /invoices/103/ HTTP/1.1" 200 762 "user=bob"
    # Bob (user 2) accediendo a facturas de usuarios 101-103 -> clara señal de IDOR

    Comandos de monitoreo:

    # Ver últimas 50 peticiones a /invoices/
    grep "/invoices/" logs/django.log | tail -50
    
    # Contar cuántos IDs distintos ha consultado una IP
    grep "192\.0\.2\.10" access.log | grep "/invoices/" | grep -o "/[0-9]*" | sort | uniq -c
    
    # Detectar usuarios que acceden a muchos objetos
    awk '{print $NF}' access.log | sort | uniq -c | sort -nr | head
    # último campo = usuario; contar accesos por user

    8. Mitigación y defensa más comunes contra IDOR

    • Validar siempre el ownership en el backend:

    # Django (correcto)
    invoice = get_object_or_404(Invoice, id=invoice_id, user=request.user)
  • Usar identificadores no guessables (UUIDs):

    • Evita secuencias predecibles: user-8a3f9d2c-e1b4-4c1f-8f3a-d5d7c1b8e9a1
    • No elimina IDOR, pero dificulta el fuzzing.
  • Control de acceso basado en rol (RBAC):

    • if request.user.role == 'admin' antes de ejecutar acciones sensibles.
  • Implementar ABAC (Attribute-Based Access Control) en sistemas complejos:

    • Evaluar políticas por atributos: departamento, geolocalización, etc.
  • No exponer IDs directos si no es necesario:

    • Mapear internamente: /docs/share/abc123 → ID 999 en DB.
  • Monitoreo de comportamiento anómalo:

    • Alertas por acceso rápido a múltiples recursos.
    • Uso de SIEMs para correlacionar acceso y roles.
  • Pruebas de autorización en cada capa:

    • No confiar solo en el frontend: el backend debe validar todo.
  • Auditar APIs con herramientas de pruebas de acceso:

    • Usar Burp Suite + Autorización extension.
    • Probar con múltiples usuarios simulados.
  • Registro completo de acceso a recursos sensibles:

    • Loggear: user_idtarget_idendpointtimestampresultado.
  • Ejemplo de middleware de autorización (Django):

    def owner_required(model, id_param):
        def decorator(view_func):
            def wrapper(request, *args, **kwargs):
                obj_id = kwargs[id_param]
                obj = get_object_or_404(model, id=obj_id)
                if obj.user != request.user:
                    return HttpResponseForbidden("Acceso denegado")
                return view_func(request, *args, **kwargs)
            return wrapper
        return decorator
    
    # Uso:
    @owner_required(Invoice, 'invoice_id')
    def invoice_detail(request, invoice_id):
        ...

    IDOR nos enseña una lección fundamental: la autenticación no es autorización.

    Aunque el usuario esté logueado, eso no significa que pueda ver todo. Validar el acceso a cada objeto, especialmente en APIs, es esencial para proteger la privacidad y la integridad de los datos.

    Aplicar controles de acceso estrictos en el backend, usar UUIDs, y auditar accesos son prácticas que detienen el 99% de estos ataques. En entornos modernos, herramientas como Burp Suite y Autorización hacen trivial detectarlos.

    Con esta base, puedes comenzar a descubrir y corregir una de las vulnerabilidades más comunes y subestimadas en aplicaciones web.

    ¡Hasta el próximo post!

    // relacionados