As a security researcher, I've learned that assumptions like this are often where vulnerabilities hide.

This post is about how I discovered an Insecure Direct Object Reference (IDOR) that allowed unauthorized access to unpublished (draft) resources — and why this class of bugs is still surprisingly common.

⚠️ All identifiers, domains, and target details have been intentionally anonymized.

What Is IDOR (Quick Refresher)

An IDOR occurs when an application:

  • Uses a direct reference (like an ID number) to an object
  • Fails to properly verify ownership or authorization
  • Allows an attacker to access or modify someone else's data

In short:

"If you know the ID, you can access the object — even if it's not yours."

The Context

The target application allows users to create listings. These listings can exist in two states:

  • Published → visible to others
  • Draft (unpublished) → visible only to the owner

Or at least… that's how it's supposed to work.

Each listing also has a messaging feature, allowing users to communicate about a listing.

That's where things got interesting.

The Assumption That Broke Everything

The application assumed:

"If a listing is unpublished, nobody else can interact with it."

But assumptions don't enforce security — authorization checks do.

I noticed that the backend API accepted requests like:

/api/listings/{LISTING_ID}/conversations

At first glance, this looked normal. But one question mattered more than anything else:

Does the server verify that I'm allowed to access this listing ID?

Testing the Boundary

To test this safely, I used two accounts:

  • Victim account → creates a listing and keeps it as a draft
  • Attacker account → completely unrelated user

From the victim account, I:

  1. Created a listing
  2. Left it unpublished
  3. Noted the listing ID from the URL

Then, from the attacker account, I:

  1. Sent a request to the conversations endpoint
  2. Replaced my own listing ID with the victim's draft listing ID
None

No tricks. No race conditions. No special headers.

Just… a different ID.

The Result

The request succeeded.

None

As the attacker, I was able to:

  • ✅ Access details of an unpublished listing
  • ✅ Send messages to that draft listing
  • ❌ Without being the owner
  • ❌ Without any authorization error

At that moment, the issue was clear:

Draft did not mean private.

Why This Matters

Unpublished resources often contain:

  • Incomplete data
  • Personal information
  • Pricing or internal notes
  • Content the owner explicitly chose not to make public

This vulnerability allowed unauthorized access to private user data, which makes it a high-impact IDOR, not a cosmetic or edge-case bug.

Root Cause (In Simple Terms)

The backend validated:

  • ✔️ "Is the user authenticated?"

But failed to validate:

  • ❌ "Does this user own or have access to this specific object?"

This is one of the most classic — and dangerous — authorization mistakes.

Lessons for Developers

If you build APIs, especially RESTful ones:

  • Never trust object IDs from the client
  • Always enforce object-level authorization
  • Treat draft, private, and unpublished data as highly sensitive
  • Test endpoints with cross-account access, not just happy paths

Lessons for Security Researchers

If you're hunting for IDORs:

  • Look for numeric or predictable IDs
  • Focus on draft, private, or hidden resources
  • Test actions beyond "view" — like messaging, updating, or triggering workflows

IDORs aren't flashy, but they're everywhere — and companies do care about them when demonstrated clearly.

Final Thoughts

This bug didn't require:

  • Advanced exploitation
  • Bypassing authentication
  • Chaining vulnerabilities

It only required asking one question:

"What happens if I access something that isn't mine?"

Sometimes, that's all it takes.