Securing Fintech & Payment Platforms — An Offensive Checklist
Payment and fintech platforms carry a different threat profile from ordinary web apps: the assets are fungible and irreversible, the flows are asynchronous and multi-party, and a single logic gap can move real money. Generic web testing misses most of it. This is the checklist we run — written from the offensive side, but it doubles as a hardening guide.
Money movement is an authorization problem first
Before anything exotic, the highest-severity fintech bugs are still broken authorization on money-moving endpoints:
- Can one user initiate a transfer from another user's account by changing an account ID? (BOLA on the source of funds is catastrophic.)
- Can a viewer or support role approve, refund, or release funds a their role shouldn't?
- In maker-checker flows, can the same identity play both roles, or approve its own request?
- Does every state transition on a payment (
authorize → capture → refund → void) re-check authorization, or only the first one?
Test authorization on every endpoint that reads a balance, moves funds, changes a payout destination, or alters a limit — as each role, and cross-account. This is where the crown-jewel findings are.
Idempotency and races: the money-printing class
Asynchronous, retried, multi-party flows make idempotency the load-bearing control. Attack it directly:
- Replay a settled request with the same idempotency key vs. a fresh key — does it double-process?
- Race the same operation from many connections in one window: duplicate refunds, double coupon/credit redemption, withdrawing the same balance twice before the first debit commits, redeeming a one-time code N times.
- Negative and boundary amounts — a refund larger than the charge, a negative transfer that credits the source, decimal/rounding abuse across currency conversion.
If the check ("does this balance cover it?") and the commit ("debit it") aren't a single atomic transaction, a race turns a legitimate one-time action into an unlimited one. Assume they aren't until proven.
Webhooks are an unauthenticated code path into your ledger
Payment providers notify your back-end of events (charge.succeeded, payout.paid) via webhooks. Attackers know this. Check:
- Signature verification. Is every webhook's provider signature verified, with the raw body, before the event mutates state? A missing or bypassable check lets an attacker POST
charge.succeededand receive goods for free. - Replay protection. Are signatures timestamped and old events rejected? Can a captured legitimate webhook be replayed to double-credit?
- Event trust. Does the handler trust amounts and IDs from the webhook body, or re-fetch the authoritative object from the provider? Trusting the body is how "I paid $0.01, the webhook says $10,000 of value" happens.
- Idempotent handling. Providers retry; a non-idempotent handler double-fulfills on the provider's own retry, no attacker required.
Reconciliation and the ledger
The ledger is the source of truth; attacks aim to desync it from reality:
- Can you create a state where goods/credit are released but the ledger never debited (fulfill-before-settle, or an error path that ships but doesn't charge)?
- Are money movements append-only and auditable, or can a mutable balance be written directly?
- Do error and timeout paths leave partial transactions — charged-not-fulfilled, or fulfilled-not-charged?
- Does currency handling use integer minor units end-to-end, or float arithmetic that rounds in the attacker's favor?
The regulated surface
Fintech carries controls generic apps don't, and each is a test target:
- KYC/onboarding bypass — can identity-verification steps be skipped by calling the final step directly, reused across accounts, or satisfied with recycled documents?
- Limits and velocity — are transaction/withdrawal limits enforced server-side and atomically, or can they be raced past?
- PCI scope — is cardholder data ever reachable outside the tokenized path? Does the front-end ever handle a PAN it shouldn't?
- Statement/data authorization — statements, transaction histories, and exports are classic IDOR targets carrying dense PII and financial data.
Sensitive data hygiene
- No card numbers, full account numbers, or secrets in logs, error messages, analytics, or client-side storage.
- Transaction and support endpoints scoped so one customer can never enumerate another's records.
- Tokens for money-movement actions short-lived and bound to the specific operation.
How we prove it
Every finding is demonstrated on throwaway test accounts and the smallest possible amount — one duplicated cent to your own balance, immediately reversed; one benign webhook replay against a test object; one cross-account read of your own second account. You prove the control failed and stop. In a domain where the asset is real money, discipline in the proof is not optional — it's the difference between a researcher and a liability.
Run this list top to bottom and the authorization + idempotency sections alone will surface more severe issues than a full generic web assessment. Money changes the threat model — test like it.