Changing an employee ID should never reveal another employee’s record. If an API accepts an ID supplied by the client but does not check whether the signed-in caller may access that specific object, it has Broken Object Level Authorization (BOLA). Sequential or otherwise predictable IDs make it easier to try many references, but they are not the underlying security failure.
The employee-directory example in this article is a generic scenario, not a claim about a particular company or breach. The same defect can affect customer profiles, invoices, vehicles, documents, projects, exports and nested API resources.
As an Amazon Associate I earn from qualifying purchases.
What the employee-directory scenario looks like
Imagine an authenticated employee using a profile endpoint such as GET /api/employees/1842. The server returns that employee’s data because the request contains the number 1842. If the same session can change the number to 1843 and receive another person’s profile, the endpoint is making an object-level authorization decision incorrectly—or not making one at all.
Free tools Windows power users keep installed
One-click scans. No signup required.
The identifier is only a pointer to a record. Seeing it in a list, notification link, browser address, error message or mobile-app request does not grant permission to use it. Authorization must be evaluated for the authenticated principal, the requested object and the requested operation every time.
#1 Best Overall
GET /api/employees/1843 HTTP/1.1
Authorization: Bearer <session-token>
A secure API might return the caller’s permitted profile, a deliberate 403 Forbidden, or a non-revealing 404 Not Found, depending on the application’s policy. What it must not do is return object 1843 merely because the caller guessed or obtained its identifier.
BOLA, IDOR and function-level authorization
Broken Object Level Authorization
OWASP uses BOLA for a missing or incorrect check on a particular object referenced by a request. The object can be identified in a URL path, query parameter, header, request body, GraphQL variable or another client-controlled field. The rule applies to every operation, not only profile reads.
Insecure Direct Object Reference
IDOR describes the familiar pattern in which a user changes a direct reference—such as an integer, UUID or filename—and reaches another user’s data. In API work, IDOR is closely related to BOLA; the practical lesson is the same: an identifier is not an access token.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Broken Function Level Authorization
BOLA is different from Broken Function Level Authorization (BFLA). BFLA occurs when a caller can invoke a function they should not be allowed to invoke at all, such as an administrator-only operation. BOLA occurs when the caller may use the general function but targets an object outside that caller’s permitted scope. An employee may be allowed to view their own profile while being forbidden to view a colleague’s profile.
Why predictable IDs make enumeration easier
Sequential numbers reduce the effort needed to discover references. An attacker who sees one valid ID can try nearby values, automate requests, or combine identifiers observed in normal application traffic. Random UUIDs and opaque strings make guessing harder, but they do not create authorization. A stolen or leaked UUID must still be rejected when the caller lacks permission.
Enumeration is an access pattern, not a separate authorization control. It becomes harmful when an endpoint accepts each altered reference without checking object ownership, tenant membership, role, relationship or another policy. A bulk endpoint can have the same defect even when no one guesses individual IDs: a request that asks for a list may return users or records outside the caller’s allowed scope.
Which operations need object-level checks?
Checking one successful GET request is not enough. Authorization can be inconsistent across methods, controllers and service layers. Review every path that reads or changes an object.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →| Operation or path | What to verify | Possible impact if unchecked |
|---|---|---|
Profile or record GET |
The caller may read this specific employee or object | Unauthorized disclosure of personal, employment or business data |
PATCH or PUT |
The caller may change the referenced object and each editable field | Data tampering, privilege changes or account compromise in some designs |
DELETE |
The caller may remove this exact object | Destruction of another user’s document, project or account data |
| Create or action requests containing an object ID | Every referenced parent, owner or target is within scope | Unauthorized assignment, sharing, transfer or other state change |
| Exports and downloads | The export query is restricted to permitted objects, not just the download route | Large-scale data disclosure |
| Nested resources | Both the parent and child relationship are authorized | Cross-tenant access through a valid child ID |
| GraphQL queries and mutations | Resolvers authorize each returned or changed object | Reading or deleting another user’s object through a mutation or resolver |
| Bulk and list endpoints | Filters, pagination and submitted ID arrays are scope-limited | Directory-wide or tenant-wide exposure without guessing each ID |
How to test for BOLA safely
Test only applications and accounts for which you have explicit authorization. Do not enumerate a public employer directory or alter someone else’s data. A controlled two-account test reveals the authorization decision without putting real users at risk.
Rank #3
- Define the approved scope. Record the hostnames, endpoints, methods, test window and data-handling rules. Stop if the authorization does not clearly cover the test.
- Create two principals with different access. Use account A and account B, ideally in different tenants or with different ownership. Create or identify one object owned by each account.
- Capture a legitimate request. While authenticated as account A, use the documented API or authorized application traffic to request A’s object. Note every reference location, including path segments, query values, JSON fields and GraphQL variables.
- Change only the object reference. Send the same request as account A while substituting B’s object ID. Keep the method, token and other fields unchanged so the result isolates object authorization.
- Repeat for each method. Test reads, updates, deletes, downloads, exports and action endpoints where the scope allows. A denied read does not prove that an update or delete path is protected.
- Check nested and alternate representations. Try authorized nested routes, such as a project’s document endpoint, and GraphQL queries or mutations when those features exist. Confirm that the parent-child relationship is checked, not merely the child ID.
- Review list and bulk behavior. Determine whether account A’s list contains only permitted records. For bulk requests, verify that mixed arrays or filters cannot pull B’s objects into the response.
- Record the decision without collecting unnecessary data. Save request and response metadata, object ownership, status code and a minimal proof. Redact personal information and stop after confirming a flaw.
OWASP’s testing guidance also recommends looking at identifiers that another session can already observe in normal responses, errors or notification links. This is a way to assess authorization paths, not permission to guess at an unrelated service.
How to prevent IDOR and BOLA in an API
Enforce the decision on a trusted server
Perform the check in the API, gateway or trusted serverless layer. Hiding an employee ID in a web interface, disabling a button or checking permissions in JavaScript cannot protect the record because a caller can send the request directly.
Scope the data lookup itself
Prefer a query that combines the requested identifier with the caller’s permitted scope. Conceptually:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallobject = repository.findOne({
id: requestedId,
permittedFor: authenticatedPrincipal
})
if object is null:
return a non-revealing denial
return object
This is safer than retrieving any row by primary key and hoping a later serializer or controller notices that it belongs to another tenant. The permitted scope may be ownership, a tenant, a manager relationship, a project membership, a delegated role or a policy combining several attributes.
Rank #4
Authorize every operation and field
Apply object checks independently to reads, creates, updates, deletes, exports and actions. For writes, also validate which fields the caller may change. A user who can edit a display name may not be allowed to change a role, payroll field, manager or account status.
Cover every access path
Centralize policy logic where practical, but verify that all controllers, background jobs, download handlers, GraphQL resolvers, nested routes and bulk services invoke it. A secure top-level profile endpoint does not protect a second endpoint that returns the same object through a report or notification link.
Use least privilege and deny by default
Make the default outcome a denial unless a policy explicitly grants access to the requested object and operation. Avoid a role-only rule such as “employee can read employees” when the real requirement is “employee can read their own record and records in an assigned team.”
Recommended Free Tools
Design bulk responses defensively
Apply authorization before assembling a list, export or search result. Do not accept a client-supplied array of IDs and then fetch all matching rows without filtering it through the caller’s scope. Pagination, sorting and search filters must not become alternate ways around the same policy.
Best Value
What identifier design can and cannot do
| Approach | Security value | Limitation |
|---|---|---|
| Sequential numeric IDs | Simple and efficient for databases | Easy to guess; increases discovery attempts when authorization is missing |
| Random UUIDs or opaque identifiers | Reduces casual guessing and automated enumeration | Does not authorize access; a leaked or observed value remains usable unless the server checks permission |
| Client-side obscuring or hidden fields | May reduce accidental exposure in the interface | Provides no reliable protection against a crafted request |
| Server-side, object-scoped authorization | Actually determines whether this principal may perform this operation on this object | Must be implemented and maintained on every access path |
Use non-sequential identifiers as defense in depth, alongside—not instead of—server-side policy enforcement.
Logging and detection
Authorization controls prevent access; telemetry helps you find inconsistent controls and investigate attempted abuse. Log the authenticated principal, endpoint, operation, object type, authorization outcome and correlation ID while minimizing sensitive values. Protect logs with the same care as application data.
Alerting can look for patterns such as one session requesting many adjacent identifiers, repeated denials across tenants, rapid changes to object references, or unusual bulk requests. Sequential-access patterns are useful investigation signals, but a lack of an alert is not evidence that authorization is correct. Logging should support review without storing full employee profiles or tokens.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsCommon fixes that fail
- “The ID is hard to guess.” Guess resistance lowers discovery probability but cannot replace a permission check.
- “The UI never shows that button.” An attacker can call the API without the interface.
- “We check the user’s role.” A role may permit the function while saying nothing about the particular object, tenant or relationship.
- “The read endpoint is protected.” Update, delete, export, nested, GraphQL and bulk paths may use different code and remain exposed.
- “A 404 means it is secure.” A denial status can reduce information leakage, but the underlying query must still enforce scope. Test behavior, not only status codes.
- “We will monitor it.” Detection is valuable for response, but it does not undo unauthorized disclosure or modification.
Developer and reviewer checklist
- For each client-supplied object reference, can you name the server-side policy that authorizes it?
- Does the lookup constrain results to the caller’s tenant, ownership or relationship before serialization?
- Are read, create, update, delete, export and action methods covered?
- Are nested routes, GraphQL resolvers, background jobs and bulk endpoints included?
- Do field-level rules prevent a permitted object edit from becoming a privilege change?
- Do automated tests use two principals with different scopes and verify both allowed and denied cases?
- Are identifiers treated as untrusted input even when they are UUIDs or hidden from the interface?
- Do logs provide enough context to detect repeated cross-object attempts without recording unnecessary personal data?
The decisive question is always: may this authenticated principal perform this operation on this particular object? If the API cannot answer that on the server for every path, predictable IDs can turn an ordinary reference into a directory-enumeration tool.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

