July 21, 2026
Unauthenticated Upload, Unauthenticated Read: How I Wrote Prescriptions Into Any Patient’s Account
A bug bounty writeup on a broken authorization flow in a healthcare mobile API - reported, fixed, and closed through a responsible…
By Sarthak Chauhan
3 min read
A bug bounty writeup on a broken authorization flow in a healthcare mobile API - reported, fixed, and closed through a responsible disclosure program.
TL;DR
An API endpoint used to upload patient prescription documents had no authentication at all. By changing a single numeric parameter in the request, I could upload arbitrary files into any patient's medical record — and read them straight back from a public storage path, also with zero credentials. The organization's security team validated the report quickly, shipped a fix, and closed it out with a reward. Details below are generalized and redacted per the program's disclosure guidelines.
Background
I was testing the mobile API layer behind a healthcare platform's patient app as part of an authorized bug bounty program. Mobile APIs are often a softer target than the web app they support- they're built for a specific client, get less scrutiny, and sometimes skip auth checks that the web equivalent enforces. That pattern held true here.
The Discovery
While mapping out the API's endpoints, I found a document upload route intended for adding prescription files to a patient's account. It expected two things in the request: a file, and an identifier for which patient the file belonged to.
Two things stood out immediately:
- The endpoint required no authentication token, session cookie, or API key of any kind.
- The patient identifier was a plain, attacker-controlled parameter- nothing tied it to a verified session.
That combination is a textbook broken object-level authorization issue, except here it applied to a write operation, not just a read.
Proof of Concept
To confirm the issue safely, I created two clearly labeled dummy PDF files and used deliberately out-of-range test identifiers- well outside any real patient ID range to avoid touching genuine accounts.
Step 1 — Upload a file under one test identifier, with no authentication:
curl -X POST "https://api.example-health.com/v1/health-records" \
-F "customer_id=<TEST_ID_A>" \
-F "file=@proof_a.pdf"curl -X POST "https://api.example-health.com/v1/health-records" \
-F "customer_id=<TEST_ID_A>" \
-F "file=@proof_a.pdf"Response:
{"result":"success","profile":"prescription/<generated>_proof_a.pdf","message":"Prescription uploaded successfully"}{"result":"success","profile":"prescription/<generated>_proof_a.pdf","message":"Prescription uploaded successfully"}Step 2 — Repeat with a different identifier, still with zero credentials:
curl -X POST "https://api.example-health.com/v1/health-records" \
-F "customer_id=<TEST_ID_B>" \
-F "file=@proof_b.pdf"curl -X POST "https://api.example-health.com/v1/health-records" \
-F "customer_id=<TEST_ID_B>" \
-F "file=@proof_b.pdf"Same success response.
Step 3 — Read both files back, no authentication required:
curl "https://api.example-health.com/storage/prescription/<generated>_proof_a.pdf"
# -> proof_a.pdf content, HTTP 200, Content-Type: application/pdfcurl "https://api.example-health.com/storage/prescription/<generated>_proof_a.pdf"
# -> proof_a.pdf content, HTTP 200, Content-Type: application/pdfBoth files downloaded cleanly, confirming the storage path was as open as the upload endpoint.
Step 4 — Confirm it wasn't specific to those two identifiers:
I repeated the upload against a handful of low, sequential test identifiers. Every single request returned a success response. No rate limiting, no authorization check, no pattern of failure anywhere in the range I sampled.
Why This Mattered
This wasn't just a data exposure bug - it was a data integrity bug in a healthcare system, which changes the risk calculus considerably:
- Patient safety risk: an attacker could inject fabricated prescription documents into a real patient's record. If clinical staff ever reference uploaded documents, that's a direct path to a real-world medical decision being influenced by attacker-controlled data.
- Confidentiality risk: every uploaded file- legitimate or otherwise- sat behind a predictable, unauthenticated storage path. Anyone who guessed or enumerated a filename could read it.
- No tenant isolation: every file lived in one flat directory with no per-user access boundary, meaning the blast radius was the entire user base, not a single account.
- Amplified by exposed API docs: the same domain exposed a full OpenAPI specification listing dozens of endpoints with no security scheme defined- effectively a roadmap for further testing had someone with worse intentions found it first.
Root Cause
Most endpoints on this API sat behind a shared authentication/version-check middleware layer. This particular upload route bypassed that layer entirely- a routing configuration gap rather than a deliberate design choice. It's a good reminder that authentication is only as strong as its weakest enforced route, not its average one.
Remediation
The organization's security team was responsive throughout. Once triaged, they:
- Added proper authentication to the upload endpoint
- Enforced authorization so a request can only act on the requesting user's own records
- Moved file storage behind an authenticated access path instead of a public one
The report was validated, the fix confirmed, and the finding closed with a reward through the program.
Lessons for Other Builders and Researchers
- Audit every route against your middleware, not your framework. A shared auth layer doesn't help if one route is wired to skip it.
- Write access deserves more scrutiny than read access. An IDOR that lets you write data is a fundamentally different threat than one that lets you view it- it affects trust in the data itself, not just its confidentiality.
- Public API documentation is part of your attack surface. An exposed spec doesn't cause a vulnerability, but it dramatically shortens the time it takes someone to find one.
A Note on Methodology
All testing was non-destructive and used clearly labeled synthetic files with out-of-range test identifiers chosen specifically to avoid impacting real patient data. No real patient records were accessed, modified, or retained at any point. This report was submitted, triaged, and remediated through an authorized responsible disclosure program, and details here have been generalized in line with the program's disclosure practices.
If you're building or auditing an API that handles sensitive records- health data especially- treat every upload path with the same scrutiny you'd give a login form. The cost of getting authorization wrong on a write endpoint is much higher than most teams assume.