Founder & Lead Engineer, RAITHub
RAITHub ships and tests production software. See QA as a Service or talk to us.
Most patient-data leaks are engineering failures you can test for before launch: broken object-level access, so one logged-in user can read another patient's record by changing an ID; an exposed API key or missing database rule; sensitive data written to logs or error trackers; and backups or exports left unsecured. Access control is the top risk. Test each with two accounts and the API, not just the screens.
This is general engineering information against OWASP-level risks, not a compliance certification and not a certified penetration test; confirm your legal obligations with your adviser. If you would rather have the checks run for you, see how RAITHub would test this below.
What causes a patient-data leak, in practice?
Leaks rarely come from a sophisticated attacker. They come from a few ordinary mistakes. Broken access control is A01, the top category, in the OWASP Top 10:2025, and its object-level form is the first risk in the OWASP API Security Top 10. The rest are secrets in the wrong place and data copied somewhere unguarded.
| Leak vector | What goes wrong | The check that catches it |
|---|---|---|
| Broken object-level access (IDOR) | User changes an ID in the URL and reads another patient | Two accounts; request each other's records via the API |
| Missing database access rule | A table has no row-level security, so any session reads all rows | CI check that every patient-scoped table has a policy |
| Exposed secret | An API key or service credential in client code or the repo | Secret scanning; inspect the shipped bundle |
| Sensitive data in logs | Full records logged to the console, Sentry or analytics | Grep logs and error payloads for identifiers |
| Unsecured backup or export | A dump or CSV export readable without auth | Request the export URL while logged out |
How do I test for the record-swap leak (IDOR)?
This is the single highest-value check. Create two patients, A and B, and confirm A can never reach B's data through any endpoint, even when the screen never offers the option. Test at the API, because the UI hides buttons but the route still answers.
// A deliberate attempt to read another patient's record must fail.
test('patient A cannot read patient B record', async () => {
const a = await signIn('patient-a@example.test') // synthetic data only
const res = await fetch(`/api/patients/${patientB.id}/records`, {
headers: { cookie: a.cookie },
})
// 403 or 404 is correct; 200 with B's data is a leak.
expect([403, 404]).toContain(res.status)
})
Write the matrix first, then automate the "Refuse" cells. Relationship matters in health apps: a clinician may read their own patients, not every patient.
| Action via API | Patient A | Treating clinician | Other clinician |
|---|---|---|---|
| Read own record | Allow | Allow (own patients) | Refuse |
| Read patient B's record | Refuse | Only if treating B | Refuse |
| List all patients | Refuse | Own panel only | Refuse |
| Export records | Own only, logged | Per policy, logged | Refuse |
The full isolation hunt is in users can see another tenant's data, and the database-level defence is in the PostgreSQL row-level security guide.
What about secrets, logs and backups?
- Secrets: run secret scanning on the repo and inspect the shipped client bundle. No service key, database URL or provider secret should be reachable from the browser. Keys belong in server environment variables, rotated if ever exposed.
- Logs and error trackers: grep application logs, Sentry events and analytics for names, record bodies and tokens. A stack trace that includes a full patient object is a leak waiting to be indexed.
- Backups and exports: request every export and dump URL while logged out, and from the wrong account. A CSV export or a database backup served without auth has leaked the whole dataset at once.
- Transport: confirm every endpoint is TLS-only and that cookies are secure and HTTP-only.
The broader application checklist is in the vibe-coded app security checklist, which maps cleanly onto any stack, and the full pre-launch version of these checks is in testing a HealthTech app before launch.
Buy, build or hire this?
| Option | Choose this when | Trade-off |
|---|---|---|
| An automated scanner or SAST tool | You want continuous, cheap coverage of known patterns | Finds some issues; misses relationship-based access bugs a human catches |
| Your own Playwright and SQL suite | A developer can own the IDOR matrix and the RLS CI check | Lowest cost; you design the two-account tests yourself |
| A certified penetration test | A customer, regulator or BAA requires a formal attestation | Necessary for sign-off; broader and costlier than functional checks |
| A managed security test or one-off audit | You want access, secrets, logs and backups checked end to end | An outside dependency; it is not a certified assessment |
Doing the checks yourself is realistic: plan 1 to 2 days for a developer who can drive the API with two accounts. The main risk of going alone is stopping at the UI, where the missing button hides an endpoint that still answers.
Why RAITHub for this
- Access boundaries are tested as code. Sundor Skin, a B2B platform RAITHub built, runs a security suite that deliberately tries to read other buyers' data, and CI fails if a buyer-scoped table lacks a row-level-security policy. It has 146 tables and 530+ tests.
- Health experience, stated honestly. RAITHub has built a healthcare scheduling app for a client; it has not shipped a regulated health product.
- Honest limits. This is application-level security testing against OWASP guidance. It is not a CREST- or PCI-certified penetration test and produces no compliance attestation. RAITHub holds no SOC 2 or ISO 27001 certification. Production and patient data stay in your own covered cloud; testing uses synthetic data.
When you don't need us
- Your app holds no patient data and the checks above are overkill.
- You already have a security engineer running these tests and a scanner in CI.
- You need a certified attestation or BAA: that requires a certified vendor and your compliance adviser, which RAITHub does not provide.
How RAITHub would test this
- Scope: a free 15-minute call on the roles, relationships and sensitive data; you confirm you are authorised to have the app tested.
- Plan: a risk map across access control, object-level authorization, secrets, logs, exports and transport.
- Test: the IDOR matrix with two accounts per role at the API, secret scanning, log and error-payload inspection, and logged-out export checks, against synthetic data.
- Deliver: a ranked bug report with reproduction steps and a suggested fix, plus the access tests as automated tests in your repository.
- What you receive: the report, the tests and a handover note; IP is yours, an NDA is standard. This is engineering QA, not a compliance attestation.
The audit is fixed-price, quoted in writing after the call. See security testing, the HealthTech industry page, and the sibling guide on the audit trail that proves who accessed what. To book it, ask for a security audit.
General engineering information only; it is not a compliance certification. Confirm your obligations with your adviser. Documentation checked on 11 October 2026.
Frequently asked questions
What is the most common cause of a patient-data leak?
Broken access control, where a logged-in user reaches data they should not, most often by changing an ID so they read another patient's record. It is the top category in the OWASP Top 10. Testing it with two accounts at the API, including the explicit refuse cases, catches it before users do.
Does this guide certify my app is compliant or secure?
No. These are engineering checks against OWASP-level risks. They are not a compliance certification, and application-level testing is not a certified penetration test. They reduce real risk and support your programme, but certification is a separate, legal matter decided with your adviser.
Why test at the API instead of the screens?
The screen hides options a user should not have, but the underlying endpoint often still answers. A tester who only clicks buttons never sees the leak; a tester who calls the endpoint directly, with the wrong account, finds it. Always drive the API.
How do secrets end up exposed in a health app?
Usually an API key or database credential placed in client code or committed to the repository, so it ships to the browser or lives in history. Run secret scanning, inspect the shipped bundle, keep keys in server environment variables, and rotate any key that was ever exposed.
Is a missing database access rule really a leak?
Yes. If a patient-scoped table has no row-level-security policy, any authenticated session can read every row once a single query path reaches it. A CI check that fails when a patient-scoped table lacks a policy turns that from a silent risk into a blocked build.
Has RAITHub shipped a regulated health product?
No. RAITHub has built a healthcare scheduling app for a client and has not shipped a regulated health product. For a regulated build, pair its engineering and testing with a compliance partner and a certified assessor.
Related posts
Ready to discuss your project?
Book a free 15-minute technical audit with our engineering team.