Skip to content
Sedat Özdemir
Writing

api-security

Unlocking Invisible Doors: IDOR and the Silent Guests at API Backdoors

Ever seen someone else's invoice just by changing a number in the URL? That's IDOR. Let’s look at why this 'old but gold' vulnerability still haunts modern APIs and how we can secure our systems.

Sedat Özdemir
· 4 dk read

Have you ever broken out in that famous cold sweat when you incremented a database ID by just 1 and saw someone else's invoice or private messages, thinking, 'They couldn't have really made it this easy'? If you haven't, you're either a very lucky systems administrator or you just haven't looked at the right endpoint yet.

Greetings, I'm Sedat. Today, I want to put something 'old but gold' on the table—a topic that still causes massive headaches in modern Single Page Application (SPA) and microservice architectures: IDOR, also known as Insecure Direct Object Reference. While the technical literature now groups it under 'Broken Access Control' in the OWASP Top 10, those of us in the field still like to call it by its familiar name.

What's the Deal with IDOR?

The issue is actually quite simple. A developer uses a unique database key (ID) clearly in the URL or the request body when providing access to a user's resource (this could be a profile page, an invoice, a message, or an order). No problem so far. The trouble starts the moment the system forgets to ask the question: 'Is the person making this request actually authorized to see the data associated with this ID?'

As a Red Teamer, I can tell you this: while we're out there chasing complex SQL injections or 0-day exploits, sometimes our biggest 'win' comes from simply changing a single digit. This can be frustrating for my friends on the defensive side, but it's the reality of the game.

A Classic Scenario: testCompany's Invoice Portal

Let's imagine a company called testCompany. After users log in, they view their invoices via this URL:

https://api.testcompany.example.com/v1/get-invoice?id=5501

In this case, let's say 5501 is my invoice ID. If I change that 5501 to 5500 or 5502 and I can see the invoice, address, or national ID number of another customer like 'Mr. Ali' or 'Ms. Ayşe,' then congratulations—we have a brand new IDOR vulnerability.

Not Just URLs: The Danger in JSON Bodies

Nowadays, everything runs on REST APIs and JSON. Naturally, IDOR has evolved too. In a modern web application, a profile update request might look like this:

POST /api/users/update-profile HTTP/1.1
Host: api.testcompany.example.com
Content-Type: application/json
Authorization: Bearer <my_token>

{
  "userId": "1337",
  "email": "[email protected]",
  "bio": "Red Team Lead at testCompany"
}

Here, the attacker (or our curious friend) sends the request with their own token but changes the userId value in the JSON to 1338 (the victim's ID). If the system only checks if the token is valid but fails to verify if the token owner has the authority to modify user 1338, they can update someone else's profile info.

Darkness Within the Code: Where Are We Going Wrong?

Usually, we encounter this kind of logic error on the backend (Pseudo-code):

# VULNERABLE EXAMPLE (Python/Flask-like)
@app.route('/api/invoice/<invoice_id>')
def get_invoice(invoice_id):
    # The system only fetches the data by ID, no ownership check!
    invoice = db.query("SELECT * FROM invoices WHERE id = ?", invoice_id)
    if invoice:
        return jsonify(invoice)
    return "Not found", 404

In the example above, the code only checks if the invoice exists. It doesn't check if current_user.id == invoice.owner_id. This is exactly where the 'invisible door' is left wide open.

Related posts