How a Series-B Fintech Closed 23 Critical API Flaws Before Their Next Raise
A representative API penetration testing engagement where a Series-B fintech found and fixed 23 critical API flaws before due diligence. Here is what we found.

Representative engagement. Client details anonymized and specifics changed under NDA.
Change one number in a URL and read a stranger’s bank transactions. That was the first thing we found, on day two, on a fintech API that moves real money. It stayed unfixed the whole year a “clean” vulnerability scan had sat in their evidence folder.
The client was a Series-B fintech, about 40 engineers, six weeks out from opening a Series C round. Their lead investor wanted a third-party security review before signing the term sheet. Their product was almost entirely an API: a mobile app and a partner web dashboard talking to the same set of REST endpoints. So they asked for API penetration testing, not a network scan and not a checkbox report.
That distinction is the whole story. The problems we found were exactly the kind automated tools walk straight past. By the end we had reported 23 critical and high issues, and the vast majority were authorization flaws. The API trusted the caller far more than it should have.
Key takeaways
- The most damaging fintech API flaws are authorization bugs (BOLA/IDOR and broken function-level auth), not injection or misconfiguration. Scanners miss almost all of them.
- In this representative engagement we reported 23 critical and high issues in six weeks, including account-to-account data exposure, a mass-assignment path to an admin role, and OTP endpoints with no rate limiting.
- API penetration testing means testing with multiple real user roles, comparing what each token is allowed to touch, and abusing the business logic. It is not firing a request list at one endpoint.
- Fixing authorization at the object level (does this user own this resource?) closed most of the criticals. The retest confirmed the fixes held.
- The company passed technical due diligence with no security finding blocking the raise.
The context: an API that moved money and trusted everyone
The stack was familiar. A Node and Express API behind a gateway, PostgreSQL, JWT access tokens, a mobile client and a partner dashboard. Roughly 180 documented endpoints, plus a handful that were not in the docs at all. There always are. Users could hold balances, send transfers, invite teammates, and link external bank accounts.
Their own read on the product was that it was “pretty solid.” They had a WAF. They ran a dependency scanner in CI. A previous vendor had handed them a clean-looking vulnerability scan the year before.
That scan is a good example of why a scan is not a pentest. It confirmed their TLS config and library versions were fine. It said nothing about whether user A could read user B’s transactions, which turned out to be the entire ballgame.
Our approach: roles, tokens, and what each one is allowed to touch
We scoped it as a gray-box test. They gave us API documentation, a Postman collection, and several test accounts spread across every role: two standard users in separate organizations, an org admin, a read-only analyst, and a partner-dashboard account. Multiple accounts per role is the single most important input for real authorization testing. You cannot prove that a user can reach data they should not without a second user’s data to reach for.
The working setup was unglamorous. Burp Suite as the proxy for the mobile traffic and the dashboard, with each account’s session saved so we could replay any request as any identity. A couple of Burp extensions to diff responses across roles. ffuf for endpoint and parameter discovery. Then a lot of manual request editing. Most of the real findings came from reading responses carefully, not from tooling.
The first pass just maps the surface: every endpoint, every parameter, every ID format, and which role is supposed to reach it. The second pass is the interesting one. We take a request that legitimately belongs to user A, replay it with user B’s token, then with the analyst token, then with no token at all, and watch what comes back.
What “testing the API” actually looked like here
Take the transactions endpoint. It accepted a numeric account ID in the path:
GET /v2/accounts/48213/transactions HTTP/1.1
Host: api.example.com
Authorization: Bearer <user-A-token>
Change 48213 to 48214, keep user A’s token, and the API returned a different customer’s full transaction history. That is Broken Object Level Authorization, listed as API1 in the OWASP API Security Top 10, and the same underlying issue people call IDOR (CWE-639). The IDs were sequential integers, so walking the entire customer base was trivial. First critical filed, day two.
It was not a one-off. The same missing ownership check turned up on statements, on the endpoint returning a linked bank account’s masked details, and on an invoice PDF download that took a document ID and did no check at all. One flaw class, several endpoints. The codebase authenticated the token but never asked whether the token’s owner actually owned the object being requested.
What we found: the highlights
Beyond the object-level bugs, a few findings are worth calling out because they show up again and again on fintech APIs.
Mass assignment to an admin role. The endpoint to update your own profile accepted a JSON body and bound it straight onto the user model. It documented name and email. It also silently accepted role. The request below promoted a standard user to org admin:
PATCH /v2/users/me HTTP/1.1
Authorization: Bearer <standard-user-token>
Content-Type: application/json
{"name":"Jordan","role":"admin"}
That is mass assignment (CWE-915), the flaw the current OWASP API Security Top 10 folds into Broken Object Property Level Authorization. It combines badly with the next one.
Broken function-level authorization. The admin-only endpoints that listed all org users and changed member permissions checked that you were authenticated, but not that you were an admin. A standard user could call them directly. Pair that with the mass-assignment bug and a single low-privilege account had a clean path to full control of an organization.
OTP and password-reset endpoints with no rate limiting. The one-time-code verification endpoint accepted unlimited attempts. A six-digit code with no lockout and no throttling is not a second factor. It is a speed bump. We brute-forced a valid code in minutes from a single IP. The password-reset flow had the same gap.
A JWT that was trusted too much. The access token carried the user’s role and org ID as claims, and at least one service made decisions off those claims without re-checking them server-side. Tokens had a long lifetime and there was no server-side revocation, so a leaked token stayed useful far too long.
None of this needed exotic tooling. It needed a second account and someone willing to ask what happens if I send this request as the wrong person.
The outcome
We delivered findings as we went rather than dumping everything at the end. That is how we run every time-boxed engagement. The BOLA issues went to their team on day two so engineers could start fixing while we kept testing. Each finding shipped with the exact request to reproduce it, the impact in plain language, and a specific fix.
Their team moved fast. The fixes were mostly structural: one ownership-check middleware that verified the authenticated user actually owned the object in the path, real role checks on every privileged route, an allow-list for writable fields instead of binding the whole body, and rate limiting plus lockout on the auth endpoints. Shorter token lifetimes and a revocation list handled the JWT problem.
We ran a full retest against the fixes. All 23 criticals and highs were closed and verified, and the new ownership middleware had also caught two lower-severity issues we had flagged. When the investor’s technical due diligence happened a few weeks later, nothing security-related blocked the round. That was the whole point: find it before someone hostile, or an acquirer’s auditor, finds it for you.
How CyberXplore helps
This is the core of what our API penetration testing service does: manual, role-aware testing of the endpoints that actually run your business, mapped to the OWASP API Security Top 10, with reproduction steps a developer can act on and a retest to confirm the fixes hold. If you are heading into a raise, a partner integration, or a compliance deadline and your product is an API, that is exactly when it pays off. Get a quote and tell us about your stack and timeline.
FAQ
Why can’t a vulnerability scanner find these API flaws?
Scanners are good at known issues: outdated libraries, weak TLS, missing headers, some injection patterns. Authorization flaws like BOLA depend on business context. A scanner has no idea that account 48214 belongs to a different customer, so a 200 response looks fine to it. Finding these needs a human comparing what different real users are allowed to access, which is the heart of API penetration testing.
How long does an API penetration test take?
It depends on the number of endpoints, the number of roles, and how much business logic is involved. A focused API test on a mid-size fintech typically runs one to three weeks of active testing plus a retest. The engagement in this piece was about two weeks of testing inside a six-week window that included fixing and the retest.
What is BOLA and why is it the top API risk?
BOLA (Broken Object Level Authorization) is when an API checks that you are logged in but not that the specific object you asked for belongs to you. It ranks first in the OWASP API Security Top 10 because it is common, trivial to exploit by changing an ID in a request, and often exposes every user’s data at once. It is the same core problem as IDOR (CWE-639).
Do you need production access or source code?
Neither is strictly required, but both help. Most of our engagements are gray-box: we work against a staging or production-like environment with multiple test accounts across every role and access to API docs. Source code can speed up root-cause analysis, but the critical findings in this case came from gray-box request testing, not code review.
What do we get at the end?
A report that leads with the risk, lists each finding with severity, exact reproduction steps, and a concrete fix, plus a retest confirming what was actually closed. We deliver criticals as we find them so your team can start remediating before the engagement ends, and we are on hand to talk fixes through with your engineers.
We are pre-raise. Is a pentest worth it right now?
Yes, and it is often the best time. Investors increasingly run technical due diligence, and an acquirer’s auditor finding a critical authorization flaw at the term-sheet stage is a far worse conversation than fixing it quietly beforehand. A clean, verified retest report is a strong signal that the team takes security seriously.



