BOLA in Fintech: One Endpoint Leaks Every Tenant
In a fintech pentest, one card endpoint let client A read client B's entire transaction history. An anonymized technical breakdown of a BOLA finding.
Security researcher and founder of No Vuln

In a pentest we recently ran for a Brazilian fintech, we found a BOLA (Broken Object Level Authorization) in a card endpoint. Client A could see client B's ENTIRE transaction history by changing a single parameter in the URL.
Estimated exposure if a real attacker had exploited it: a leak of the transaction history of the entire active customer base — millions of transactions, with a full mapping of card, amount and merchant. In data protection terms, that's financial personal data belonging to hundreds of thousands of data subjects. Under Brazil's data protection law (LGPD), the potential fine is up to 2% of revenue (capped at R$ 50,000,000 per violation).
This post is the anonymized technical breakdown of what happened — because the pattern repeats in nearly every Brazilian fintech we audit. If you run B2B SaaS, a fintech, a marketplace or any multi-tenant platform, this is required reading.
The discovery
The client hired us for a full grey box pentest of the platform. We received credentials for 2 test accounts — let's call them Account A and Account B. Both were legitimate corporate accounts, each with corporate cards assigned.
During the initial mapping, we identified the endpoint that served card transaction history:
GET /api/v2/cards/{card_id}/transactionsAuthorization: Bearer <account_token>
Expected behavior: each account only sees transactions for its own cards.
Actual behavior: replacing {card_id} with a UUID of a card belonging to Account B, the server returned the full history — for Account A, authenticated with its own token.
The bug in detail
The implementation only validated:
- ✅ That the Bearer token was valid (any authenticated user passed)
- ✅ That the
card_idexisted in the database - ❌ That the
card_idbelonged to the authenticated user's tenant
The 3rd check — the essential one — simply didn't exist. The endpoint trusted that if you knew the card's UUID, you had the right to see its history.
That is BOLA (Broken Object Level Authorization) — the #1 vulnerability in the OWASP API Security Top 10. It's not an obscure bug. It's one of the most exploited vectors in real-world API attacks in 2026. See the detailed technical breakdown of the 3 flaws that dominate APIs in BOLA, BOPLA and BFLA.
Why this bug shows up so often
Three architectural patterns set the stage for it:
Pattern 1: an ORM that hides the query
When the ORM makes it easy to write a query without the tenant filter, it looks correct in code review but opens BOLA at runtime. For example, in Python/Django:
- ❌
Card.objects.get(id=card_id)— no tenant filter - ✅
Card.objects.get(id=card_id, tenant_id=user.tenant_id)— with the filter
The wrong line looks “correct” in code review. Whoever writes it is focused on the resource ID, not on the authorization context. At runtime, BOLA is wide open.
Pattern 2: “middleware” authorization that isn't enough
Many applications implement authentication middleware (it validates the token) and assume that is authorization. It isn't.
- Authentication answers: who are you?
- Authorization answers: what are you allowed to do?
Without middleware that validates resource ownership, authorization becomes the developer's responsibility in each endpoint — and at some point someone forgets.
Pattern 3: UUIDs give a false sense of security
Developers often rationalize: “A UUID has 128 bits. An attacker won't guess it.”
True — the attacker doesn't guess it. But the attacker:
- Gets UUIDs from legitimate responses (listings, outgoing webhooks, logs)
- Finds UUIDs leaked in shared URLs
- Collects UUIDs from third-party integrations
- Gets UUIDs through other bugs (info disclosure, IDOR in other endpoints)
Obscurity ≠ security. A UUID is not a substitute for authorization.
How a researcher finds it
In a serious pentest, BOLA is the first place we look. The process:
- Create 2 accounts in different tenants (essential — you can't do it with 1 account)
- Exhaustively map every endpoint that receives an ID/UUID in the URL or body
- For each endpoint, swap the ID for one that belongs to the other tenant
- Observe the response — 200 OK with data = BOLA confirmed
- Document with a reproducible PoC — exact request, exact response, calibrated severity
An automated scanner does NOT do this. It doesn't juggle multiple sessions, doesn't create accounts, doesn't correlate data across tenants. Only a human researcher catches it.
Across the Brazilian B2B SaaS platforms we audited in 2026, BOLA in at least 1 endpoint appears in about 85% of engagements.
How to fix it — defense in depth across 4 layers
Layer 1: tenant-scoping middleware
A decorator or middleware that validates ownership automatically:
@tenant_scoped(resource='card', param='card_id')before the view- Automatic validation before any view logic runs
- Centralizes authorization in 1 place — doesn't depend on the developer remembering
Layer 2: queries with a mandatory tenant filter
Every SQL query on a tenant resource must include tenant_id in the WHERE:
SELECT * FROM transactions WHERE card_id = $1 AND tenant_id = $2- A SQL lint rule or ORM helper that rejects a query without
tenant_id
Layer 3: Row-Level Security (PostgreSQL)
Defense in depth at the database: PostgreSQL blocks natively via CREATE POLICY tenant_isolation. If Layers 1 and 2 both fail, Layer 3 still protects you.
Layer 4: automated authorization tests in CI
A test suite that creates 2 accounts in different tenants and attempts cross-access on every sensitive endpoint. The pipeline fails if a BOLA is introduced in a PR. This is the definitive preventive control.
The cost of not fixing it
In the real case, we estimated:
- Volume of exposed data if exploited: ~12M transactions (the whole active base)
- LGPD category: financial personal data (highly sensitive)
- Maximum LGPD fine: R$ 50,000,000 (cap per violation)
- Notification cost: mandatory reporting to the ANPD (Brazil's National Data Protection Authority) plus every affected data subject
- Litigation cost: class actions (via Procon, Brazil's consumer protection agencies, and public civil actions)
- Estimated churn: 4 in 10 Brazilian customers don't come back after an incident (Akamai, 2024)
The bug was fixed within 72h of our report. As far as anyone knows, it was never exploited by a real attacker.
What to do today
If your platform is multi-tenant (B2B SaaS, fintech, marketplace, e-commerce with sellers, healthtech with clinics):
- Audit the endpoints that receive an ID/UUID — list them all by hand
- For each one, verify: is there a tenant_id filter on the server?
- If you're not sure: commission a pentest focused on cross-tenant BOLA
- Implement the 4 layers above as an architectural standard
If you're a founder/CTO thinking “this can't happen to us, our team is good” — that's exactly what every team says right before discovering BOLA in production 6 months later.
The difference between a good team and a vulnerable team isn't talent — it's an adversarial validation process.
To talk about a pentest focused on multi-tenant isolation, see the dedicated pages: fintech penetration testing, SaaS penetration testing and marketplace penetration testing. To understand more about the flaw classes that rule APIs in 2026, see BOLA, BOPLA and BFLA.
Next step
Want to apply this to your system?
No Vuln runs in-depth penetration tests with the same methodology described in this article. Request a proposal: mutual NDA within 24h, scope defined on a technical call.