10-minute read
The review that changed how I look at REST API security in banking was not a breach. Nothing leaked. A team had shipped a transactions endpoint for a mobile app, the pen test came back clean, the OAuth flow was textbook, and the JWT validation was correct down to the clock skew. Then someone on the platform side asked a simple question: what happens if I change the account number in the URL?
The answer was that you got somebody else’s statement. Every check had passed. The token was real, the signature was valid, the scopes were right. Nobody had asked whether the person holding the token owned the account they were reading. That gap is where most banking API security failures live, and no password policy, MFA rollout or WAF rule closes it.
I have watched some version of this play out in more than one estate since, and the scale has changed. UPI alone cleared 24.51 billion transactions in August 2026, worth about 29.8 lakh crore rupees. One country, one month. At those volumes an endpoint that leaks one record per request is not a bug. It is a data export facility.
TL;DR
- Most banking API breaches are authorization failures, not authentication failures. A valid token for account 1001 that also reads account 1002 is the pattern.
- Plain bearer tokens are a liability the industry has under-priced. FAPI 2.0 (in force in UK Open Banking, Brazil and Australia’s CDR; the reference profile elsewhere) answers with sender-constrained tokens via mTLS or DPoP, so a stolen token is useless without the client’s key.
- The audit is the floor, not the goal. Three controls carry the real weight: object-level authorization, sender-constrained tokens and idempotent payments.
Banking API Security: Why Authorization Isn’t Authentication
Authentication answers “who are you?” A JWT issued to user X proves the caller is X. Authorization answers “are you allowed to touch this?” Does X own the resource in the URL? Most banking API security failures I have reviewed sit in the gap between those two questions. The team validates that the token is genuine and then forgets to validate that it grants access to the specific object being requested.
The pattern is always the same. A developer adds authentication to GET /accounts/{accountId}/transactions. Valid JWT? Return the transactions. The code never checks whether the token’s subject actually owns accountId. An attacker, properly logged in as user A, increments the number and reads B, C and D. OWASP calls this Broken Object Level Authorization, API1 in the 2023 API Security Top 10, and it has held the top slot for a reason.
My view: BOLA is not a developer mistake, it is an architecture smell. If your framework has no ownership-check primitive and your code review does not test authorization separately from login, you will ship it on every endpoint.
Threat Model: OWASP API Top 10 Meets /accounts and /payments
Four items on the OWASP list map directly to banking REST APIs. API1, Broken Object Level Authorization, covered above. API2, Broken Authentication: weak token validation, low-entropy state parameters, tokens without expiry checks. A token lifted off a compromised device that stays valid for days is API2.
API4, Unrestricted Resource Consumption: a password-reset endpoint that accepts unlimited requests fills inboxes and locks customers out. Per-IP rate limiting alone does not save you here, because corporate NAT puts hundreds of real users behind one address; one abuser blocks the whole office. How fast your gateway scales under that kind of flood is partly a runtime question, which I covered in Quarkus native vs JIT for banking. API7, Server-Side Request Forgery: a merchant-logo endpoint that fetches arbitrary URLs will happily fetch 169.254.169.254 and hand back cloud credentials.
None of these are exotic. Regulators expect you to know them and to be able to show the control for each.
Regulatory Map: Who Requires What
Here is where I see the most confident wrong statements, so I will be careful. FAPI 2.0 Security Profile has been final since February 2025 and is in force where an ecosystem adopted it: UK Open Banking, Brazil’s Open Finance and Australia’s Consumer Data Right. Elsewhere it is the reference profile people design to, not a legal requirement. PSD2 RTS requires Strong Customer Authentication and secure communication between banks and third-party providers; it does not name FAPI. That framework is on its way out: PSD3 and the accompanying Payment Services Regulation reached provisional political agreement in November 2025, and the PSR applies directly twenty days after it appears in the Official Journal, with PSD3 transposed into national law by roughly Q2 or Q3 of 2028. The PSR makes a dedicated data interface from the bank to third-party providers mandatory rather than optional, and reworks the SCA exemptions around risk. If you are building EU-facing APIs today, design against the PSR, not against PSD2 as it stands. PCI DSS 4.0 replaced 3.2.1 on 31 March 2024, with its future-dated requirements becoming mandatory on 31 March 2025. The requirement that matters for this article is 6.2.4: bespoke and custom software must be engineered to prevent, among others, injection, business-logic and access-control attacks. That is BOLA, in auditor language.
On India: the Account Aggregator technical specifications (mutual TLS between entities, detached JWS signatures on requests and responses) come from ReBIT and the Sahamati ecosystem, not from a single RBI direction. RBI’s September 2025 Master Direction consolidates payment-aggregator rules and is a separate instrument. If you are building AA-facing APIs, the ReBIT spec is the document to read.
⚖️ Regulatory note. FAPI 2.0 is adopted in UK Open Banking, Brazil and Australia’s CDR. Elsewhere it is recognised as the reference profile. PSD2 RTS requires Strong Customer Authentication and secure communication but does not explicitly name FAPI. RBI does not mandate FAPI. This article is an engineering explainer, not compliance advice; confirm with your own compliance team and regulator whether sender-constrained tokens are required for your jurisdiction and use case.
Sender-Constrained Tokens Beat Bearer, Every Time
A bearer token is exactly what the name says: whoever bears it, wins. Steal one and you have full API access until it expires. Sender-constrained tokens change the deal. An mTLS-bound token (RFC 8705) is worthless without the same TLS client certificate. A DPoP-bound token (RFC 9449) is worthless without the client’s ephemeral private key, because every request carries a fresh signed proof that the resource server verifies against the token. The attacker has the token but cannot prove possession of the key, so every call fails.
FAPI 2.0 also closes the front door. It requires Pushed Authorization Requests (PAR, RFC 9126): the client posts the authorization request to the server over a back channel first and receives a short-lived reference, so parameters never travel through the browser where they can be tampered with. The profile explicitly rejects authorization requests that arrive without PAR. Signed request objects (JAR) still exist, but under FAPI 2.0 they belong to the separate Message Signing profile for non-repudiation, not to the baseline. I have seen more than one FAPI 2.0 “gap analysis” that missed PAR entirely.
| Control | What a stolen artefact buys | Client complexity | Where it sits |
|---|---|---|---|
| Bearer token | Full API access until expiry | Trivial | Legacy; not permitted under FAPI 2.0 |
| mTLS-bound token (RFC 8705) | Nothing without the client certificate | PKI, certificate lifecycle, careful TLS termination | FAPI 1.0 and 2.0; typical for server-to-server and AA-style flows |
| DPoP-bound token (RFC 9449) | Nothing without the client’s private key | Per-client key generation; no certificates | FAPI 2.0; the practical choice for mobile and SPA clients |
| Pushed Authorization Request (RFC 9126) | Nothing; the request never transits the browser | One extra back-channel POST | Required by FAPI 2.0 Security Profile |
| Idempotency key | A replayed request returns the original result instead of a second debit | Unique constraint plus TTL on the server | Every payment API; de-facto standard, not a regulation |
| JWS message signing | Tampered payloads fail verification; provides non-repudiation | Key management on both sides | FAPI 2.0 Message Signing profile; ReBIT AA spec |
Code: Idempotency Key + DPoP on POST /payments
Note the scheme on the first header. RFC 9449 defines Authorization: DPoP, not Bearer. Send a DPoP proof alongside a Bearer header and a compliant resource server will reject it.
POST /payments HTTP/1.1
Host: api.bank.example
Authorization: DPoP eyJ0eXAiOiJhdCtqd3Qi... (access token, cnf.jkt bound)
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIs... (proof JWT: htm, htu, iat, jti, ath)
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 100.00,
"currency": "INR",
"beneficiary": "ACCT-5678"
}
# Server-side order of checks:
# 1. Idempotency-Key seen before? Return the stored response. Never re-execute.
# 2. Verify DPoP proof: signature against the JWK in its header, htm/htu match
# this request, jti unseen, iat fresh, ath == SHA-256 of the access token.
# 3. Confirm the proof's key thumbprint equals the token's cnf.jkt claim.
# 4. Only now: authorize (does token.sub own the debit account?), then execute.
Step 4 is the one teams skip. Sender constraint proves the caller holds the key. It says nothing about whether that caller may debit this account.
What Goes Wrong in Practice
None of the incidents below is a bank. I use them because they are public, documented and show the exact failure modes above without needing me to break an NDA.
Aadhaar via the Indane LPG portal (2018–2019, India). Reportedly a hard-coded access token on a customer-lookup endpoint with no rate limit. Enumeration at national scale. That is API2 and API4 together, and it is the closest public analogue to a bank’s customer-lookup API.
T-Mobile (disclosed January 2023). According to the company’s SEC filing, a single API was abused to pull data on roughly 37 million accounts over about six weeks before detection. The public description points to an API returning more than the caller was entitled to see, which is the BOLA shape.
Optus (September 2022, Australia). Reportedly an unauthenticated endpoint exposed around 9.8 million customer records. No token at all, so not even authentication was in place. It is here to make one point: inventory matters. You cannot secure an endpoint you do not know you have.
An Eleven-Point Scorecard for Your API Estate
| Control | What a “Yes” means | Status |
|---|---|---|
| OAuth 2.1 with PKCE | Every client, every flow. Implicit flow gone. | ☐ Yes ☐ Partial ☐ No |
| Object-level authorization | Ownership check on every /accounts/{id}-style endpoint, tested separately from login. |
☐ Yes ☐ Partial ☐ No |
| Sender-constrained tokens | DPoP for mobile and web, mTLS for service-to-service. No plain bearer tokens. | ☐ Yes ☐ Partial ☐ No |
| Pushed Authorization Requests | Authorization server rejects front-channel requests without PAR. | ☐ Yes ☐ Partial ☐ No |
| TLS 1.2+ end to end | 1.3 preferred. Certificate pinning on TPP-to-bank paths. | ☐ Yes ☐ Partial ☐ No |
| Rate limits after auth | Per user or API key, not just per IP. Tighter on login and payment. | ☐ Yes ☐ Partial ☐ No |
| Schema validation at the gateway | OpenAPI enforced; oversized or malformed payloads rejected. | ☐ Yes ☐ Partial ☐ No |
| Logging and alerting | PCI DSS Requirement 10. Alert on failed-auth spikes and enumeration patterns. | ☐ Yes ☐ Partial ☐ No |
| Idempotency keys | Mandatory on every state-changing payment call. | ☐ Yes ☐ Partial ☐ No |
| API inventory | Shadow API scanning plus a published deprecation policy. | ☐ Yes ☐ Partial ☐ No |
| Penetration testing | OWASP API Top 10 and business-logic abuse, at least every six months, with object-level authorization tested as its own case. | ☐ Yes ☐ Partial ☐ No |
Scorecard reflects the author’s field experience, not a regulatory framework. Weight it against your own risk assessment.
Key Takeaways
- Test authorization separately from authentication. A green pen test on login proves nothing about
/accounts/{id}. - Retire plain bearer tokens. DPoP for public clients, mTLS for confidential ones. Steal the token, get nothing.
- PAR is part of FAPI 2.0, not optional. If your gap analysis does not mention RFC 9126, it is incomplete.
- You cannot secure an endpoint you do not know you have. Shadow API discovery and a published deprecation policy belong in the same programme as the token work, not in a later phase.
- Idempotency is a security control. A retried debit is a double debit unless the server remembers.
Conclusion & Next Steps
That transactions endpoint I opened with was fixed in an afternoon. One ownership check, one integration test that logs in as A and asks for B’s account. The expensive part was not the fix; it was the year the gap sat there behind a clean pen-test report. That is the argument of this piece about banking API security: audits check that controls exist, and existing is not the same as protecting the right thing.
If you own a banking API estate, do three things this quarter. Run the scorecard honestly and put the “No” rows on a roadmap with owners. Pick one high-value endpoint and write the negative authorization test today. Then open your FAPI 2.0 gap analysis and search for “PAR”. If you are also deciding where that estate should run, my assessment of open-source private cloud for banks covers the infrastructure side of the same question.
Passing the audit is the floor. Object-level authorization, sender-constrained tokens and idempotent payments are what hold the estate up afterwards.
Frequently Asked Questions
Do we need to move from bearer tokens to DPoP or mTLS now?
If you operate in UK Open Banking, Brazil or Australia’s CDR, FAPI 2.0 already applies and sender-constrained tokens are required. Elsewhere there is no single rule naming DPoP or mTLS, but PCI DSS 4.0 (6.2.4, mandatory since 31 March 2025) expects engineered defences against access-control attacks, and plain bearer tokens are a weak answer. Treat it as a roadmap item with a date, not a debate.
BOLA sounds like a one-developer mistake. How does it affect a whole API program?
Because it repeats. If the framework has no ownership-check primitive and code review does not test authorization separately, the same omission ships on endpoint after endpoint. The fix is structural: a shared authorization helper, a negative test per resource type, and a review gate that asks “who owns this object?” for every new route.
We rate-limit by IP. Is that enough?
No. Corporate NAT and mobile carriers put many real users behind one address, so a per-IP limit either blocks legitimate customers or is set too loose to stop abuse. Limit per authenticated user or API key after login, keep a strict per-IP limit only on unauthenticated endpoints, and set tighter budgets on login and payment than on reads. The exact numbers depend on your traffic, not on a blog post.
If FAPI 2.0 allows mTLS or DPoP, can we pick one and ignore the other?
Usually you end up with both. mTLS suits confidential clients such as backend-to-backend and Account Aggregator flows where certificates are already managed. DPoP suits mobile apps and browser clients that cannot hold a certificate. A bank with both kinds of client needs both, and either way PAR is required on the authorization request.
