BOLA and IDOR describe missing object permissions
An object reference might be the ID of a report in the path /reports/bericht-7. It identifies the report the client means. IDOR stands for Insecure Direct Object Reference: the controllable reference leads to access without the required permission. BOLA, Broken Object Level Authorization, denotes missing or defective object-level authorisation in the OWASP API Security Top 10. The terms overlap considerably; remediation depends on the omitted decision.
The reference can also appear in a JSON field, search condition or GraphQL argument. A hard-to-guess UUID makes blind guessing harder but grants no access. An ordinary sharing link may already reveal an ID. OWASP therefore requires a decision about the specific action and object, regardless of the identifier's format. OWASP API1:2023.
Distinguish this from function permissions: may this person export reports at all? And field permissions: may they see internal comments in an otherwise permitted report? All three decisions may be needed for the same call.
Describe permitted access before changing code
‘Everyone sees only their own data’ suits a personal notebook. For a team product, that rule would often be wrong: a colleague may read a shared report but not delete it. A support account may have an explicitly limited special permission. Such exceptions need a concrete rule instead of blanket administrator access.
For a fictional report API, the decision could look like this:
On narrow screens, scroll the table sideways.
| Situation | Read | Change | Reason |
|---|---|---|---|
| Active member, own report in the current tenant | Yes | Yes, while the report is editable | Ownership plus permitted state |
| Active member with explicit read access | Yes | No | The grant covers reading only |
| Another member without a grant | No | No | The same tenant alone is insufficient |
| Person from another organisation | No | No | No cross-tenant access is intended |
| Former member with an earlier grant | No | No | Active membership is missing |
The table illustrates a product contract, not a universal mandatory rule. Explicitly include public objects, shared resources and service accounts in your own table. A deliberately public catalogue needs different rules from an invoice.
Bind the decision to actual data access
The server needs a verified identity, the requested action and trustworthy information about the object. A client-supplied user_id, owner_id or tenant_id is initially a claim or selection. It must not itself provide permission.
Place the rule where every relevant access path passes through it. An application may restrict eligible records in the database query or check a loaded object through a central policy before output and modification. What matters is that no direct, nested or later-added path omits the rule. Also check lists, search, exports, download links, bulk operations and background jobs. OWASP IDOR Prevention.
For a change, permission must still match the change actually performed. If a grant can be withdrawn between checking and saving, for example, clarify permitted concurrency. An appropriate transaction, lock or conditional database operation may be needed; an early middleware check alone does not resolve this state.
A small example: permission to read is not ownership
The following pure Python function illustrates only the example application's read rule. principal already contains current membership data verified server-side; report comes from your own data store. Unverified request fields must not replace these prerequisites.
def may_read_report(principal, report):
if not principal["active_member"]:
return False
if principal["tenant_id"] != report["tenant_id"]:
return False
return (
principal["id"] == report["owner_id"]
or principal["id"] in report["reader_ids"]
)
The function allows a named reader access without making them the owner. Changes would need a different rule. A separate selection of returned fields also follows it. The example includes no authentication, database, error response, revocation cache or transaction protection. It has been checked locally with synthetic permitted and forbidden cases; it is not a complete authorisation module.
Missing or invalid policy data must not grant access. Define how the application fails closed on these errors and logs them distinctly internally. Do not expose another object's details in the error response.
Test permitted and forbidden cases through the same flow
In an isolated development or test environment, prepare two identities of your own, separate test objects and an intentional sharing grant. Use the normal application path with the same policy as in operation. Know the expected business effect for every row of your rule.
On narrow screens, scroll the table sideways.
| Negative test with your own test data | Meaningful evidence |
|---|---|
| Owner reads their report | Expected content, permitted fields only |
| Named reader reads the same report | Sharing works; write access remains separately blocked |
| User without a grant supplies the same ID | No protected data and no unauthorised side effect |
| Membership or sharing grant is withdrawn | No further access after the defined update interval |
| List, export and individual retrieval access the same objects | The same business boundary, including pagination and embedded objects |
A 403 or 404 may fit the error contract. Status alone does not prove the rule, however: a response could still contain data, and a change could already have occurred before rejection. For writes, therefore, also inspect stored state and triggered jobs. The error reference explains status selection.
Introduce tighter rules in a controlled way
First compare the new rule with the product's legitimate sharing arrangements. A blanket owner filter can unintentionally disable existing teamwork. Retain known permitted flows as positive cases and add forbidden counterparts. Document which paths and object types remain uncovered.
If rollout blocks valid access, investigate the specific rule or membership data. Safe rollback options include a previously tested restrictive policy or temporarily disabling the affected function. Removing the object check entirely would not be a safe fix. OWASP recommends denying permissions by default and preserving enforcement through tests. OWASP Authorization.
What the public check can answer here
The API Security Check does not test signed-in identities, object grants or changes to your records. A clean header or good grade therefore provides no evidence against BOLA or IDOR. This requires your own permission specification, review of code and data access, and appropriate behavioural tests.
For multiple organisations, tenant isolation covers databases, caches and jobs. The test your own API security guide places individual evidence in a bounded overall testing approach. For an existing public scanner finding, reading HTTP findings explains what was actually observed.
Sources and date
Sources checked: 27 September 2026. Links also appear alongside the statements they support.