July 26, 2026
Beyond SSL Pinning: When Changing One Number Broke an App’s Authorization
The best bugs I’ve found rarely came from clever exploits. They came from staring at a normal-looking request and wondering, “What if I…
By Varshith Reddy
5 min read
The best bugs I've found rarely came from clever exploits. They came from staring at a normal-looking request and wondering, "What if I just change this?"
Most mobile assessments start the same way. You get past SSL pinning, you point the traffic at a proxy, and you finally get to watch the app talk to its backend. It feels like the milestone — the thing you were working toward. In reality, it's the warm-up. The interesting part starts once you can see the conversation and begin questioning the assumptions behind it.
That's exactly how a recent authorized engagement unfolded. Getting past pinning gave me visibility, but the finding that mattered had nothing to do with interception. It came from watching how the backend decided who was allowed to see what — and realizing it wasn't really deciding at all.
What follows is the walkthrough of a Broken Object Level Authorization (BOLA) issue: how I found it, why it happens, and why this unglamorous bug class keeps topping the API security charts.
Every app-specific detail here has been stripped out or swapped for placeholders. This is about the method and the reasoning, not any particular target.
The Distinction That Everything Hinges On
Before the technical part, one idea sits underneath this entire finding, and it's worth being precise about.
A mobile app authenticates, gets a token, and attaches that token to every call it makes to the backend. Simple enough:
Mobile App
│
HTTPS + Auth Token
│
▼
Backend API
│
▼
DatabaseMobile App
│
HTTPS + Auth Token
│
▼
Backend API
│
▼
DatabaseTwo different controls are supposed to be at work here, and people conflate them constantly:
- Authentication asks who are you?
- Authorization asks what are you allowed to touch?
An enormous share of real-world API bugs come down to apps that nail the first and quietly skip the second. The server confirms you're a legitimate, logged-in user — then cheerfully hands over data without ever checking whether that data belongs to you. It's a subtle gap, and it's precisely where this one lived.
Working Method
I don't go looking for bugs on the first pass. I go looking for understanding. The engagement followed a deliberate order:
- Learn how the app behaves normally
- Get eyes on the encrypted traffic
- Map how it communicates with the backend
- Study request shapes and parameters
- Spot which inputs the client controls
- Probe how the server handles authorization
The payoff of this patience is that when a flaw does surface, it doesn't feel like luck — it feels inevitable. You've already built the mental model that makes the gap obvious.
Getting Eyes on the Traffic
The target did what any reasonably mature app does: it enforced certificate pinning. Rather than trusting whatever CAs live in the device's trust store, the app only accepted its own backend's certificate. That's a genuinely good defense against man-in-the-middle attacks, and it meant my proxy saw nothing but encrypted noise to start with.
In an authorized lab setup, I used dynamic instrumentation to temporarily disable certificate validation — strictly to make testing possible — and the traffic opened up in the proxy.
The objective at this point was visibility. Nothing more.
With requests now readable, I could actually study the app's behavior: how it logged in, how it fetched profiles and user data, which identifiers the backend leaned on, and what its authorization model looked like in practice. A black box had turned into something I could reason about — and something started to stand out.
The Thing That Caught My Eye
Plenty of requests referenced user-specific resources using an identifier supplied by the client. Nothing exotic — a shape you've seen a thousand times:
GET /api/profile?id=<USER_IDENTIFIER>
Authorization: Bearer <ACCESS_TOKEN>GET /api/profile?id=<USER_IDENTIFIER>
Authorization: Bearer <ACCESS_TOKEN>On its face, unremarkable. Apps request resources by ID all day long. But it's the kind of pattern that should always prompt one question:
Is the server actually confirming that this user is allowed to read this object — or is it just trusting the ID it was handed?
That question ran the rest of the assessment.
Testing It
The test was almost embarrassingly simple, which is exactly what makes BOLA such a persistent problem.
I didn't touch authentication at all. Same account, same valid token, everything legitimate. The only thing I changed was the object identifier pointing it at a resource that belonged to a different test account.
Authenticated as: User A
Requested object: User B's resourceAuthenticated as: User A
Requested object: User B's resourceIf authorization were doing its job, the server should have shut me down a 401, a 403, an empty response, or a bland "not found." Any of those would have been a healthy sign.
Instead, it returned User B's data.
I ran it again against other authorized test accounts and got the same result each time. Not a fluke, not an edge case — a consistent, reproducible authorization failure.
Why It Happened
This is textbook Broken Object Level Authorization the flaw you'll also see called IDOR.
The part worth emphasizing: authentication was flawless. The token was valid, the session was real, the user was exactly who they claimed to be. The breakdown was purely in the authorization layer.
Stripped down, the vulnerable logic did this:
User is authenticated
↓
Return the requested object ← no ownership checkUser is authenticated
↓
Return the requested object ← no ownership checkWhat it needed to do:
User is authenticated
↓
Does this object belong to this user?
↓
Yes → return data
No → denyUser is authenticated
↓
Does this object belong to this user?
↓
Yes → return data
No → denyThe server treated a client-controlled identifier as if it were an authorization decision. But an ID is just an input, and anything the client supplies, the client can change. Trusting it is like checking someone's ticket to enter the building and then letting them into any room they can name.
Why It Matters
BOLA rarely stops at one harmless field. Depending on the endpoint, someone walking identifiers could pull:
- Personal information
- Account metadata
- Preferences and settings
- Activity history
- Internal identifiers
- Sensitive business data
Even short of account takeover, quietly reading other users' records at scale is a privacy, compliance, and reputation problem all at once. That's the whole reason BOLA sits at the top of the OWASP API Security Top 10 it's easy to introduce, trivial to exploit, and often completely invisible until someone bothers to change a number.
Recommendations for Developers
The good news is that this class of bug is very preventable. A few habits go a long way:
Enforce authorization on the server, every time. A client-supplied identifier is never proof of access. Verify ownership on the backend for every request.
Default to least privilege. An authenticated user should only ever reach what they own, unless you've deliberately granted more.
Check access at the object level on every endpoint. Don't assume an earlier gate covered it each endpoint serving user-owned data needs to validate access on its own.
Return only what's needed. Trim responses to the fields the client actually uses. Don't ship internal attributes "just in case."
Test authorization while you build. Fold identifier-swapping into your API testing. Holding authentication constant and changing the object reference exposes these gaps fast.
Review access rules across the whole surface. Whether you're running custom middleware or a managed backend, enforcement has to be consistent one unguarded endpoint quietly cancels out all the others.
Closing Thoughts
The findings I'm proudest of usually aren't the technically flashy ones. They come from understanding how a system was built and poking at the assumptions baked into it. This engagement was a clean reminder that good testing leans more on curiosity and a feel for trust boundaries than on any particular tool.
Pinning gave me a view into the conversation. A single well-aimed question turned that view into a finding.
Whether you build software or break it for a living, BOLA is worth internalizing, because it reduces to one stubborn principle:
Authentication tells the server who you are. Authorization decides what you're allowed to reach. Get that line right, and you've closed off one of the most common ways applications leak.
Thanks for reading. Test only what you're authorized to, and disclose responsibly.