Hunting BOLA: How to Audit Your API Security with OWASP ZAP

Security tutorial - IT technology blog
Security tutorial - IT technology blog

The Invisible Threat in Your API

You’ve spent an entire 40-hour sprint hardening your OAuth2 flow and rotating JWT keys. You feel safe. But for many APIs, the front door is locked while the back windows are wide open. The biggest threat often isn’t a hacker breaking into the system—it’s a legitimate user accessing data they don’t own. This is Broken Object Level Authorization (BOLA), and it has claimed the #1 spot on the OWASP API Security Top 10 for years.

Security isn’t a “nice-to-have” feature you bolt on at the end. After watching a botnet hammer my server with SSH brute-force attempts at 3:00 AM, I stopped treating security as a “Phase 2” task. That experience proved that hackers always look for the path of least resistance. In modern applications, that path is usually a poorly protected API endpoint that trusts whatever user_id or order_id you throw at it.

Get ZAP Running in Under 5 Minutes

OWASP ZAP (Zed Attack Proxy) is the industry-standard utility for finding these holes. Unlike old-school web scanners that just crawl HTML links, ZAP consumes API definitions to map out your application’s hidden corners.

1. Grab the Installer

Download ZAP for your OS. Once you launch it, check the Marketplace (the colorful box icon) to ensure the OpenAPI Support add-on is active. You’ll need this to talk to modern APIs.

2. Fast-Track with API Definitions

If your API has Swagger documentation—usually found at /swagger.json or /api-docs—importing it is the fastest way to start. It beats manual exploration every time.

  • Navigate to Import > Import an OpenAPI definition from a URL.
  • Paste your documentation URL.
  • Watch as ZAP populates the “Sites” tree with every single endpoint.

3. Start with a Baseline Scan

Right-click your API host in the “Sites” tab, then select Attack > Active Scan. This catch-all scan hunts for SQL injection and XSS. However, it usually misses BOLA because BOLA requires an understanding of who owns what data. We need to go deeper.

The Anatomy of a BOLA Attack

BOLA happens when your server trusts the ID in the URL without checking the session owner. It’s the digital equivalent of a hotel key card that opens every door on the floor.

Think of an endpoint for fetching invoice details:

GET /api/v1/invoices/9901

If I’m logged in as User A, I should see invoice 9901. But what happens if I change that number to 9902? If the server only checks if I’m logged in (Authentication) but fails to check if invoice 9902 is actually mine (Authorization), you have a data breach on your hands.

Why Traditional Scanners Fail

Automated tools love patterns. They recognize a 500 error or a database leak instantly. But a BOLA exploit looks like a perfectly valid request. The server returns a 200 OK with valid JSON. To a simple tool, that looks like success. To a business, that’s a leak of sensitive customer records.

Fuzzing for BOLA with ZAP

To find BOLA, we mimic an attacker who systematically guesses IDs. ZAP’s Fuzzer is the tool for this job.

Step 1: Capture the Request

Find a request in ZAP’s “History” tab that includes an ID, such as /api/users/me/profile or /api/orders/101.

Step 2: Set Up the Fuzz

  1. Right-click the request > Fuzz…
  2. Highlight the numeric ID (e.g., 101) and click Add…
  3. Select Series in the Payloads window to generate a range from 100 to 1,000.
  4. Click Start Fuzzer.

Step 3: Spot the Anomalies

Notice the Size Resp. Body column in the Fuzzer tab. If 99% of your requests return a 400-byte “Forbidden” message but one returns 12KB of data, you’ve found a smoking gun. That 12KB response is likely data belonging to another user.

Pro-tip: Access Control Testing

For professional audits, use the Access Control Testing add-on. You can define two users (User A and User B), record a session as User A, and tell ZAP to replay it using User B’s token. If User B successfully pulls User A’s private data, the tool flags it automatically.

# A manual BOLA test using cURL
# User A's token trying to grab User B's record
curl -H "Authorization: Bearer <User_A_Token>" \
     "https://api.example.com/v1/private-data/user-B-id"

Fixing the Root Cause

Finding a vulnerability is a great win, but your real job is closing it for good. This requires changing how you think about data access.

1. Stop Using Client-Side IDs

If a user wants their own profile, don’t rely on /api/users/123. Use /api/users/me. Your server should extract the user ID directly from the secure session or JWT, making it impossible for a user to guess a different ID.

2. Enforce Ownership in Every Query

Every database query must include an ownership check. Don’t just fetch by ID; fetch by ID and Owner.

# Secure
def get_order(order_id, current_user_id):
    # This query ensures the order belongs to the person asking
    return db.query("SELECT * FROM orders WHERE id = ? AND user_id = ?", 
                    order_id, current_user_id)

3. Switch to UUIDs

Sequential IDs (1, 2, 3…) are an invitation for attackers to scrape your entire database. Using UUIDs like 550e8400-e29b-41d4-a716-446655440000 doesn’t fix the logic bug, but it makes “guessing” IDs virtually impossible.

4. Automate Security in your Pipeline

Don’t wait for a manual yearly audit. Run OWASP ZAP in a headless Docker container as part of your GitHub Actions. This ensures that every pull request is checked for basic API flaws before a single line of code touches production.

# Running ZAP API scan in a CI/CD pipeline
docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable zap-api-scan.py \
    -t https://api.example.com/swagger.json -f openapi -r report.html

Building secure APIs is an iterative process. By combining ZAP’s automation with a “verify everything” mindset, you can keep your users’ data safe from the most dangerous API exploit in the wild.

Share: