IDOR
An API that takes an object identifier from the client and returns the object without checking whether the caller is allowed to have it. Change the number, get someone else's data.
An insecure direct object reference is an endpoint that accepts an identifier for an object and returns that object without verifying that the authenticated caller is entitled to it. The modern API-security literature calls the same thing broken object level authorization, and it is consistently the most commonly found API flaw.
GET /api/v1/device/1042/config -> your device, correctly
GET /api/v1/device/1043/config -> somebody else's device, also returned
Why it keeps happening
Authentication and authorization get conflated. The endpoint checks that you are logged in, which is authentication, and then trusts the identifier you supplied to decide what to hand back, which skips authorization entirely. The check that is missing is one line: does object 1043 belong to the caller?
It is also invisible to most automated scanning. Nothing is malformed, no error is raised, no signature matches. The response is a perfectly valid 200 containing data. Finding it requires two accounts and the willingness to compare.
Where it shows up on IoT platforms
This class of device is unusually exposed to it, because the mobile app and the device both talk to the same cloud API and the identifiers are rarely secret:
- Device serial numbers or MAC addresses as identifiers. Both are printed on the device, broadcast over the air, or sequential.
- Camera and video endpoints.
/api/stream/<device_id>returning a live feed to any authenticated account is a recurring, serious finding. - Firmware and configuration downloads.
/api/device/<id>/firmwaredisclosing another unit's configuration, which frequently contains its credentials. - Registration and pairing. Endpoints that claim a device by identifier, which turns an IDOR into a takeover rather than a disclosure.
- The mobile app's traffic is the map. Proxy the app, and every endpoint and identifier format is handed to you.
Testing it
- Create two accounts, each owning its own object.
- As account A, capture the request for A's object.
- Replay it with B's identifier, keeping A's session.
- Compare: same data, filtered data,
403, or404.
Then repeat across every verb, not just GET. PUT, DELETE and POST on the same path are frequently protected inconsistently, and a read-only IDOR is a disclosure while a write IDOR is a takeover. Nested and secondary identifiers matter too: /api/user/<me>/device/<other> often checks only the first one.
What does not fix it
- Unguessable identifiers. Replacing
1042with a UUID makes discovery harder and changes nothing about authorization. Identifiers leak: through logs, referrers, sharing features, the mobile app, and any endpoint that lists them. - Hiding the endpoint. The app knows it, so an attacker does.
- Checking on the client. The mobile app disabling the button does not stop the request.
The fix is a server-side ownership check on every object access, ideally enforced centrally so it cannot be forgotten on a new endpoint, rather than written out per handler.
Pitfalls
- Testing with an admin account, which legitimately sees everything and hides the flaw.
- Reading a
200as success without checking the body; some APIs return an empty envelope with a success code. - Reporting one endpoint. IDOR is nearly always systemic across an API, and the report should say so.
- Doing this against a production platform you do not own. Use your own accounts and your own devices.
What it pairs with
rest covers the API style this appears in, mitmproxy and mitm are how you see the app's requests, and tls is what stands between you and that traffic.