November 1, 2026
The Hidden IDOR Vulnerability in /me/ API Endpoints
A Practical Bug Bounty Guide to Finding Broken Object Level Authorization in Modern APIs

By 0xSinsber
7 min read
A Practical Bug Bounty Guide to Finding Broken Object Level Authorization in Modern APIs
Most developers assume that an endpoint starting with /me/ automatically returns data belonging to the currently authenticated user.
But what happens when the server doesn't actually enforce that assumption?
An endpoint designed to return personal resources may expose other users' private data if authentication and object-level authorization are not properly implemented.
In this article, we'll explore how to identify these vulnerabilities, investigate suspicious API behavior, distinguish genuine IDOR/BOLA issues from false positives, and report findings responsibly.
This guide is based on a practical testing methodology using HTTP requests and API authorization analysis. All examples use fictional endpoints and data.
1. Understanding IDOR and BOLA
Insecure Direct Object Reference (IDOR) is a vulnerability that occurs when an application allows users to access objects they are not authorized to access by manipulating an object reference.
Today, this issue is commonly discussed under the broader term Broken Object Level Authorization (BOLA).
For example, consider an API endpoint:
GET /api/users/1001/ordersGET /api/users/1001/ordersIf an authenticated user can change 1001 to 1002 and retrieve another user's orders without proper authorization, the application has an object-level authorization vulnerability.
The problem isn't that the ID is predictable.
The problem is that the server fails to verify whether the requesting user is authorized to access the requested object.
This distinction is important:
- Authentication determines who the user is.
- Authorization determines what that user is allowed to access.
An application can implement authentication correctly while still having serious authorization vulnerabilities.
2. Why /me/ Endpoints Deserve Special Attention
Modern applications frequently use /me/, /my/, or /self/ prefixes to represent resources belonging to the current user.
Examples include:
GET /api/me/profile
GET /api/me/orders
GET /api/me/collections
GET /api/me/addresses
GET /api/me/payment-methodsGET /api/me/profile
GET /api/me/orders
GET /api/me/collections
GET /api/me/addresses
GET /api/me/payment-methodsThese routes are convenient for developers because they avoid explicitly passing a user ID.
However, the route name itself provides no security guarantee.
Consider this simplified backend implementation:
Vulnerable implementation:
def get_collection(collection_id):
collection = db.get_collection(collection_id)
return collectiondef get_collection(collection_id):
collection = db.get_collection(collection_id)
return collectionThe application retrieves the requested collection without checking who owns it.
Safer implementation:
def get_collection(collection_id, current_user):
collection = db.get_collection(collection_id)
if collection.owner_id != current_user.id:
raise Forbidden()
return collectiondef get_collection(collection_id, current_user):
collection = db.get_collection(collection_id)
if collection.owner_id != current_user.id:
raise Forbidden()
return collectionThe second implementation explicitly verifies ownership before returning the resource.
The important lesson:
A /me/ endpoint must derive the current user from a trusted authentication context and enforce authorization on every resource access.
3. A Practical Methodology for Testing /me/ Endpoints
When testing an application during an authorized bug bounty engagement, I recommend following a structured approach.
Step 1: Map the API Endpoints
Start by browsing the application normally and observing its API traffic.
Look for endpoints containing:
/me//my//self//account//profile//collections//orders/
Pay particular attention to endpoints that return user-specific resources.
For example:
GET /api/me/collections
GET /api/me/collections/12345GET /api/me/collections
GET /api/me/collections/12345Record the endpoints, HTTP methods, parameters, and object identifiers.
Burp Suite Proxy and HTTP history are useful for this stage.
Step 2: Test Without Authentication
Capture a legitimate request while logged in, then replay it without authentication credentials.
Remove:
- Cookie
- Authorization
- Session tokens
- Other authentication-related headers
Example:
GET /api/me/collections HTTP/2
Host: example.com
Accept: application/jsonGET /api/me/collections HTTP/2
Host: example.com
Accept: application/jsonExpected behavior:
HTTP/2 401 UnauthorizedHTTP/2 401 UnauthorizedAn anonymous request should not receive private account information.
Potentially vulnerable behavior:
HTTP/2 200 OK
Content-Type: application/json
{
"data": [
{
"id": 12345,
"name": "Summer Wishlist"
}
]
}HTTP/2 200 OK
Content-Type: application/json
{
"data": [
{
"id": 12345,
"name": "Summer Wishlist"
}
]
}A successful response containing private user data is a serious warning sign.
However, HTTP 200 alone does not establish an IDOR vulnerability. The response must be examined to determine whether authentication is missing, whether the data is public, and whether unauthorized object access is possible.
Step 3: Rule Out False Positives
Before reporting anything, investigate whether the response is actually generated for the anonymous request.
Useful checks include:
1. Repeat the request
Send the same request multiple times and compare the responses.
2. Use an invalid session value
For example:
Cookie: session=invalid_random_valueCookie: session=invalid_random_valueIf the application still returns the same private data, this provides additional evidence that the endpoint may not validate the session.
3. Inspect caching behavior
Review response headers such as:
Cache-Control
Age
X-CacheCache-Control
Age
X-CacheLook for signs that a CDN or intermediary cache may be returning an earlier response.
4. Compare independent sessions
Where possible, use separate test accounts and compare their responses.
The goal is to determine whether the application is returning genuinely unauthorized data rather than a public response, shared resource, or stale cached object.
Step 4: Investigate Object-Level Authorization
This is where the investigation becomes more interesting.
Suppose the API returns a collection identifier:
{
"id": 12345,
"name": "Summer Wishlist"
}{
"id": 12345,
"name": "Summer Wishlist"
}The application also exposes:
GET /api/me/collections/{id}GET /api/me/collections/{id}Test whether the server checks ownership when retrieving an individual collection.
The safest approach is to use two accounts that you control.
Account A
Creates a collection:
{
"id": 12345,
"name": "Account A Collection"
}{
"id": 12345,
"name": "Account A Collection"
}Account B
Attempts to access the same collection:
GET /api/me/collections/12345 HTTP/2
Host: example.com
Cookie: session=ACCOUNT_B_SESSIONGET /api/me/collections/12345 HTTP/2
Host: example.com
Cookie: session=ACCOUNT_B_SESSIONExpected behavior:
HTTP/2 403 ForbiddenHTTP/2 403 ForbiddenOr an appropriate 404 Not Found response that does not disclose the resource.
If Account B receives the private collection belonging to Account A, you have strong evidence of BOLA.
Repeat the comparison in the opposite direction to verify the authorization boundary.
Step 5: Investigate Potential Write Access
Read access is not the only possible impact.
Some applications expose operations that modify resources:
PUT /api/me/collections/12345
POST /api/me/collections
DELETE /api/me/collections/12345PUT /api/me/collections/12345
POST /api/me/collections
DELETE /api/me/collections/12345If ownership validation is missing from a read endpoint, related write operations deserve investigation too.
A write authorization vulnerability could allow an attacker to:
- Rename another user's collection.
- Add or remove saved products.
- Modify account-specific resources.
- Delete another user's data.
However, a missing authorization check on GET does not automatically prove that PUT, POST, or DELETE are vulnerable.
Likewise, unusual resource names or suspicious existing values are not sufficient evidence of unauthorized modification.
Only test write operations using resources and accounts you control, and only when the program explicitly permits those tests.
If you discover suspicious evidence during read-only testing, document it as an observation rather than presenting it as a confirmed write vulnerability.
Step 6: Evaluate the Actual Impact
Not every IDOR has the same severity.
Consider the following examples:
Finding Potential impactAccess to public information Informational or no security impact Access to another user's non-sensitive preferences Low to Medium Access to private collections or personal information Medium to High Access to sensitive financial or account information High Unauthorized account takeover or widespread destructive access Potentially Critical
The final severity depends on the sensitivity of the exposed data, the number of affected users, exploitability, and the actual confidentiality, integrity, and availability impact.
Do not assign Critical severity simply because an identifier is sequential or a write endpoint exists.
Use CVSS and the bug bounty program's severity guidelines to justify your assessment.
4. Common Mistakes When Testing IDOR
Several mistakes can lead to incorrect conclusions.
Mistake 1: Assuming HTTP 200 means IDOR
An endpoint may return public data, an empty response, or a shared resource.
Always establish what the returned object represents.
Mistake 2: Confusing Missing Authentication with BOLA
An endpoint returning private information without authentication demonstrates a missing authentication or access-control issue.
To establish object-level authorization failure, investigate whether the application improperly allows access to resources belonging to another user.
These weaknesses can overlap, but they are not identical.
Mistake 3: Relying only on Sequential IDs
Predictable identifiers make enumeration easier, but an unpredictable UUID does not prevent IDOR.
The server must enforce authorization regardless of the identifier format.
Mistake 4: Assuming Read Access Means Write Access
Being able to retrieve another user's resource does not prove that you can modify or delete it.
Keep confirmed findings separate from potential additional impact.
Mistake 5: Testing Against Real Customers
Never modify, delete, or unnecessarily collect another user's private information just to strengthen a report.
A responsible researcher proves the security boundary failure using the minimum evidence necessary.
5. How to Write a Strong IDOR Report
A useful report should make it easy for the security team to reproduce and understand the issue.
Include the following:
Title
Describe the authorization failure and its impact.
Example:
Unauthenticated BOLA on /api/me/collections Exposes Private User Data
Affected Endpoints
List every confirmed vulnerable endpoint.
Authentication Context
Clearly explain whether the request was authenticated, unauthenticated, or made using another test account.
Steps to Reproduce
Provide the exact HTTP requests and relevant responses.
Expected vs. Actual Behavior
Explain what the application should have returned and what it actually returned.
Impact
Describe what an attacker can access and why the information matters.
Evidence
Include sanitized request/response captures and screenshots where useful.
Testing Limitations
State what you did not attempt, especially if you avoided modifying or deleting other users' resources.
A report with a clear authorization boundary failure is much stronger than one that simply states that changing an ID returns HTTP 200.
6. How Developers Can Prevent These Vulnerabilities
Preventing BOLA requires enforcing authorization on the server side.
Here are five important recommendations.
1. Authenticate protected endpoints
Endpoints intended for private user resources must reject anonymous requests.
2. Enforce ownership checks
Every resource access should verify that the current user is authorized to access the requested object.
3. Scope database queries to the current user
Instead of retrieving an object globally and checking it later, consider querying through the authenticated user's authorized resource scope.
Example:
collection = db.collections.find_one({
"id": collection_id,
"owner_id": current_user.id
})
if collection is None:
raise NotFound()
return collectioncollection = db.collections.find_one({
"id": collection_id,
"owner_id": current_user.id
})
if collection is None:
raise NotFound()
return collection4. Apply authorization to every HTTP method
GET, POST, PUT, PATCH, and DELETE operations all require appropriate authorization checks.
5. Add authorization tests
Test access using:
- No authentication.
- Invalid authentication.
- The resource owner's session.
- Another user's valid session.
Authorization tests should cover every endpoint and every operation that accesses private resources.
Using UUIDs instead of sequential IDs may reduce guessability, but it is not a replacement for proper authorization.
7. Final Checklist for Bug Bounty Hunters
Whenever you encounter a /me/, /my/, or /self/ API endpoint, use this checklist:
- Identify endpoints returning user-specific resources.
- Capture legitimate requests using your own account.
- Replay requests without authentication.
- Test invalid session values.
- Inspect response headers for caching behavior.
- Identify object references and resource identifiers.
- Use two accounts you control to test object ownership.
- Compare responses between authorized and unauthorized sessions.
- Investigate related read and write operations within program scope.
- Document confirmed impact separately from assumptions.
- Prepare a reproducible report with sanitized evidence.
Conclusion
The /me/ prefix is a naming convention, not a security mechanism.
A route can look perfectly safe while its backend implementation fails to enforce authentication or object-level authorization.
For bug bounty hunters, this creates an opportunity to investigate an often-overlooked part of API security.
For developers, it is a reminder that authorization must be enforced at the resource level, not assumed from the endpoint name.
Never trust what an endpoint is called. Verify what the server actually allows.