⏱️ Reading time: 16 min
In July 2026, two researchers demonstrated that with nothing more than a car’s license plate they could locate it, unlock it, and start it remotely, without ever touching the vehicle. The flaw wasn’t in the hardware, but in the manufacturer’s API: the pattern they exploited (a broken object level authorization flaw) repeats in any system that connects a physical object to a user account. The researchers are keeping the brand secret until the fix is complete, but the mechanism is already a reusable case study for anyone who builds or audits connected platforms.
📑 En este artículo
- TL;DR
- What is object level authorization?
- Why it matters in IoT systems and connected platforms
- How the pattern works: from a public data point to full control
- How to reconstruct an API’s map from the app
- Practical examples: the same flaw in your own API
- Comparison: authorization models against this pattern
- Real-world cases of the same pattern
- Common mistakes and best practices
- Going deeper: BOLA beyond simple CRUD
- Frequently Asked Questions
- References
TL;DR
- A broken object level authorization flaw lets any authenticated user control another user’s resources just by changing an ID in the URL.
- The chain of license plate, VIN, subscriber GUID, and account links public data to internal APIs without validating who actually owns each piece of data.
- BOLA has topped the OWASP API Security risk ranking since 2019 and still holds that spot in the 2023 edition.
- Validating resource ownership at every endpoint, not just the identity in the token, closes most of these cases.
- Two test accounts and an HTTP client are enough to audit whether your own API suffers from this same pattern.
What is object level authorization?
Object level authorization is the control that checks whether the person making a request has permission over that specific resource, not just whether they’re authenticated. Without that control, changing an identifier in the URL is enough to access another account’s data.
The name comes from the OWASP API Security Top 10, which classifies it as BOLA (Broken Object Level Authorization) and distinguishes it from a simple authentication failure. Authentication answers who I am; authorization answers what I, specifically, can touch out of everything that exists in the system. A server can get the first part perfectly right and fail completely at the second.
The concept isn’t new. The web security community has known it for more than a decade as IDOR (Insecure Direct Object Reference). OWASP renamed it for its API guide because in a REST or GraphQL API the object almost always travels explicitly in the URL or request body, which makes the pattern even easier to find and exploit.
Why it matters in IoT systems and connected platforms
A broken object level authorization flaw in an online store leaks another customer’s order. The same flaw in a connected car platform hands over physical control of the vehicle: unlocking it, starting it, honking the horn, or tracking its live location. The failing code is identical, an endpoint that doesn’t validate ownership, but the consequence shifts into a completely different category.
That’s why OWASP ranks BOLA first in its API Security Top 10 since the 2019 edition, and keeps it there in the 2023 revision. It’s not an exotic bug: according to that same document, it’s the most commonly reported cause in real-world API incidents, because any endpoint that accepts an identifier as a parameter is a candidate.
Connected platforms make the problem worse because they chain together systems with their own identifiers. The app talks to the telematics backend, that backend talks to an identity provider, and the provider talks to the billing system. Each link is a chance to forget to ask the same question again: does this user have permission over this specific object, or did they just prove who they are?
How the pattern works: from a public data point to full control
The attack doesn’t start by stealing a password. It starts by reconstructing the calls the official app makes: the app package gets decompiled, the base URLs get located, and with that, the API map is laid bare. That reconstruction, on its own, doesn’t violate anyone else’s account yet; the problem shows up in the next step.
With the endpoint map in hand, the pattern advances in layers. First, a public data point (the license plate) gets converted into an internal identifier (the VIN) through a state registry, something legitimate because honest uses exist for that lookup, like checking a used car’s history. Second, that internal identifier gets checked against an administrative endpoint that, in the case the research documented, required no more authentication than a hardcoded API key found inside the app’s own JavaScript bundle. Third, that key returns a subscriber identifier, and the subscriber identifier returns the real owner’s email and account data. Fourth, and last, that email lets the attacker obtain or force a valid token on the central identity platform, the same token the legitimate app uses to unlock, start, or track the car.
No step in that chain requires a hardware vulnerability. Each step is, on its own, an API answering correctly a poorly posed question: who holds this identifier?, instead of who has permission over this identifier?. Swap car for smart lock, security camera, or health wearable and the chain is exactly the same.
sequenceDiagram
participant At as Attacker
participant Reg as Public registry
participant Adm as Admin API
participant Idp as Identity platform
At->>Reg: queries the license plate
Reg-->>At: returns the VIN
At->>Adm: sends the VIN with the APK's hardcoded key
Adm-->>At: returns subscriber GUID and email
At->>Idp: requests a session with the obtained email
Idp-->>At: valid access token
Note over At,Idp: the token unlocks full control of the vehicle
How to reconstruct an API’s map from the app
Before finding any authorization flaw, you need to know which endpoints exist, and most mobile apps expose them unintentionally. An APK or an IPA can be unpacked with free tools (jadx for Android, for example) and read almost like near-complete source code: that’s where the base URLs, parameter names, and, in the worst cases, embedded API keys show up.
The second technique, more direct, is intercepting real traffic with a proxy like mitmproxy or Burp Suite while using the app normally: open the app, look at the car on the map, ask it to honk the horn. Each of those actions fires an HTTP request that the proxy captures in full, headers, token, and body included. Replaying that request while changing only one identifier is, literally, all it takes to test whether an API authorization flaw exists, with no need to see a single line of server code.
Neither technique breaks anything: decompiling your own app or intercepting your own traffic with a test account is, in practice, the same audit an attacker would run, just without crossing the line of touching someone else’s account. It’s actually the first recommended step before hiring an external pentest. If the internal team can’t reconstruct its own API from the app, it won’t be able to check that every endpoint validates ownership either.
⚠️ Heads up: a hardcoded API key inside a mobile app’s bundle isn’t a minor detail. Anyone can extract it by decompiling the package, so it should never be the only barrier protecting an administrative endpoint.
Practical examples: the same flaw in your own API
To see the pattern without relying on any specific manufacturer, a typical endpoint from a custom-built connected vehicle platform is enough. Here’s what the vulnerable version looks like in a Node backend with Express:
app.post('/api/v1/vehicles/:vehicleId/unlock', authenticate, (req, res) => {
const vehicle = db.vehicles.findById(req.params.vehicleId);
vehicle.unlock();
res.json({ status: 'unlocked', vehicleId: vehicle.id });
});
The authenticate middleware confirms the token is valid, but nobody confirms that the URL’s vehicleId belongs to the token’s user. An attacker authenticated as user A can call the same endpoint, swapping the ID for one of user B’s vehicles:
curl -X POST https://api.connectedfleet.dev/v1/vehicles/3b7e2f-b2-user-b/unlock \
-H "Authorization: Bearer $USER_A_TOKEN"
On the vulnerable server, the response is a 200 with someone else’s car already unlocked:
{"status":"unlocked","vehicleId":"3b7e2f-b2-user-b"}
The fix doesn’t add a new library or an external service: it adds a comparison.
app.post('/api/v1/vehicles/:vehicleId/unlock', authenticate, (req, res) => {
const vehicle = db.vehicles.findById(req.params.vehicleId);
if (!vehicle || vehicle.ownerId !== req.user.id) {
return res.status(403).json({ error: 'forbidden' });
}
vehicle.unlock();
res.json({ status: 'unlocked', vehicleId: vehicle.id });
});
With that comparison in place, the same request from user A against user B’s car returns this:
{"error":"forbidden"}
As a diagram, the decision that one extra line adds is always the same, regardless of framework:
flowchart TD
A["Request with object_id"] --> B["Authenticate token"]
B --> C{"Does the resource exist?"}
C -- No --> D["404 Not Found"]
C -- Yes --> E{"Does the resource's owner_id match the token's user_id?"}
E -- No --> F["403 Forbidden"]
E -- Yes --> G["Execute action"]
It’s literally one extra line per endpoint. The cost of skipping it is that any API client becomes an administrator over every account, not just their own.
Comparison: authorization models against this pattern
Not every object access control mechanism costs the same or scales the same way. This table summarizes the most common options:
| Model | When to use it | Advantage | Limitation |
|---|---|---|---|
| Client-trusted ID (anti-pattern) | Never in production | Trivial to implement | Any authenticated user can access any object |
| Per-endpoint ownership check | Small apps or monoliths | Low cost, easy to read in the code | Has to be repeated in every handler; a new endpoint can miss it |
| Centralized authorization layer (middleware or policy engine) | Microservices with many endpoints | A single source of truth for the whole API | Adds latency and one more piece to operate |
| Relational permission model (ReBAC, Zanzibar-style) | Platforms with hierarchies: fleets, organizations, teams | Scales to complex permissions and inheritance | Higher design complexity from day one |
Real-world cases of the same pattern
The connected car platform case documented by this research isn’t isolated. In early 2023, researcher Sam Curry published, along with his team, a series of equivalent flaws in the APIs of several car manufacturers, using the same kind of chain: a public or semi-public identifier (the VIN) that unlocked access to accounts and, from there, remote commands over the vehicle.
The pattern isn’t limited to cars either. Any platform that links a device identifier to an account (smart locks, home security cameras, shared bike or scooter fleets, health wearables that expose medical history) repeats the same structure. An endpoint receives an ID and assumes, without checking, that whoever sends it has a right to it.
What changes between industries is the severity of the consequence, not the technical cause. Broken object access control on a connected medical device can leak a diagnosis; on a lock, it can open a physical door. The failing code, again, is the same endpoint without an ownership check.
That’s why the lesson doesn’t depend on the anonymized brand in the original case: any team maintaining a fleet of connected devices, whether owned directly or integrated through a third-party API, can audit its own platform with the same methodology without waiting for an outside researcher to publish it first.
Common mistakes and best practices
Most of these flaws don’t come from general negligence, but from a series of small shortcuts that pile up. Here are the most frequent ones:
- Trusting the ID the client sends: validating the token and assuming that any resource it asks for belongs to it, without ever asking again.
- Sequential or guessable IDs: numbering vehicles, users, or orders starting from 1 makes it easy to enumerate every object in the system, even when an ownership check exists.
- Hardcoded API keys on the client: any secret embedded in a mobile app or SPA is, for all practical purposes, public. An administrative endpoint should never depend on that alone.
- Testing only the happy path: a team’s tests usually cover a user accessing their own resource and rarely cover a user trying to access someone else’s.
- Internal tools without the same rigor: support or admin panels tend to get less security review than the public-facing API, despite having broader permissions.
Best practices mirror each mistake: use non-guessable identifiers (UUID v4) as a first layer, require every handler to check ownership and not just identity, rotate and restrict any key that lives on a client, and add at least one automated test per endpoint that checks cross-account access between two test accounts. None of these measures requires rewriting the entire architecture: all of them can be applied endpoint by endpoint, alongside the rest of the work.
💡 Tip: the cheapest test for this pattern is keeping two fixed test accounts (A and B) in your CI environment and running every endpoint that takes an ID using A’s token but B’s resource. If anything returns 200 instead of 403 or 404, there’s a broken object level authorization flaw to fix before merging.
Going deeper: BOLA beyond simple CRUD
A single REST endpoint is the easiest example to explain, but not the only place this flaw shows up. In GraphQL, a single query can request several objects in the same request through an array of IDs. It’s common for the resolver to validate only the first object in the list, or none at all, and assume the rest share the same owner. The result is a variant of the same bug, much harder to catch with manual testing because there’s no single URL to change, just a JSON payload with several fields.
flowchart LR
Q["GraphQL query with 3 object_id"] --> V1["Verify object 1"]
Q --> V2["Verify object 2"]
Q --> V3["Verify object 3 (sometimes skipped)"]
V1 --> OK1["Authorized"]
V2 --> OK2["Authorized"]
V3 --> FAIL["No check: exposes another user's data"]
It’s worth distinguishing this flaw from its close relative, broken function level authorization (BFLA). BOLA answers whether a user can touch this specific object; BFLA answers whether a user can run this action, regardless of the object, like a delete endpoint that should only exist for admins but responds the same way to any role. A single system can have both problems at once; tests for one don’t cover the other, so it’s worth testing them separately.
It’s also worth separating this from a pure authentication flaw, like a fixed API key shared across an entire app. Extracting a hardcoded key from an APK, something anyone can do with free decompilation tools, is a secrets management problem, not an authorization one. But, as the connected car case’s chain showed, both flaws often combine: the leaked key opens the door, and the missing ownership check means that once inside, there’s no internal wall stopping lateral movement between accounts.
As an additional control, when fixing every endpoint right away isn’t feasible, per-account rate limiting and alerting on anomalous access patterns (the same token requesting hundreds of different IDs within minutes) don’t close the root flaw, but they do reduce how much it can be exploited before someone notices. They don’t replace the fix; they support it while the fix rolls out.
Your next step: pick a real endpoint in your API that takes an ID as a parameter, set up two test accounts, and confirm with a cross-account request whether it returns 403 or lets you in.
Frequently Asked Questions
What exactly is BOLA?
BOLA (Broken Object Level Authorization) is the name OWASP uses for the flaw where an API checks who is making the request, but not whether that person has permission over the specific object they’re requesting. It shows up when the only control is the token’s authentication, not the relationship between the user and the resource.
Is BOLA the same as IDOR?
In practice, they’re the same problem under two names from different eras. IDOR originated in the context of classic web applications; BOLA is the term OWASP adopted for its API Security Top 10 because, in an API, the object almost always travels explicitly in the URL or payload.
Is it enough to use UUIDs instead of sequential IDs?
No. A UUID makes it harder to guess another user’s identifier, but it doesn’t replace the ownership check. If someone obtains another user’s UUID through any other means, like a log or a shared link, the authorization flaw remains exactly as open.
Are hardcoded API keys in a mobile app part of the same problem?
They’re a related but distinct flaw: a fixed key on the client is a secrets management problem, not an authorization one. The BOLA pattern shows up afterward, once that key or another method has already let someone in and nobody checks again whether they have a right to the specific object they’re requesting.
How is this different from an authentication flaw?
Authentication answers who you are; object access control answers what you can touch, specifically, out of everything that exists in the system. A server can authenticate perfectly and still let any valid account control every other account’s resources.
What should I do if I find this flaw in a third party’s API?
Report it through the manufacturer’s responsible disclosure channels, like a security.txt file, a bug bounty program, or a published security email, and give them reasonable time to fix it before publishing technical details. That’s actually exactly what the researchers in the case behind this article did: keeping the brand secret until the fix is complete.
References
- shubs.io: original research on the chain of authorization flaws that allowed control of connected cars from an undisclosed manufacturer.
- OWASP API Security Top 10: document that has ranked BOLA first in the API risk ranking since 2019.
- CWE-639 (MITRE): formal definition of Authorization Bypass Through User-Controlled Key, the technical root of this pattern.
- Sam Curry: 2023 research on equivalent flaws in multiple car manufacturers’ APIs.
📱 Enjoying this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion
Featured image: Foto de Sasun Bughdaryan en Unsplash
Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.
Leave a comment
0 Comments