The Target
A telehealth platform that connects students and members with healthcare providers for virtual visits, therapy sessions, and prescription management. The platform handles Protected Health Information (PHI) including prescription records, visit histories, and care records.
The Discovery
I created two test accounts on the platform. Neither account had any prescriptions. When I hit the prescriptions endpoint with Account A's token, the API returned 1,352 prescription records belonging to 86 different members. Same request with Account B's token returned the exact same dataset.
The endpoint required authentication (returning 401 without a token) but applied zero authorization filtering after that. Any authenticated user got every prescription record in the system.
What Was Exposed
Prescription details: Medication names, dosages, NDC codes (National Drug Codes), quantities, directions, refill counts, and prescription status. Medication names alone reveal medical conditions.
Patient identifiers: Internal member IDs, external member IDs, provider IDs, and visit IDs for each prescription. These link prescriptions to specific patients and their telehealth visits.
Pharmacy information: Full pharmacy details including store name, street address, city, state, ZIP, phone, and fax. This reveals the patient's physical location.
Cross-reference potential: The 463 unique visit IDs exposed in the prescription data could be used with other endpoints like video token generation or care record signed URLs to access additional records.
The Proof
Account A: zero prescriptions, received 1,352 records from 86 members. Account B: zero prescriptions, received the exact same 1,352 records. Meanwhile, other member endpoints properly returned 403 when accessing cross-account data. The prescriptions endpoint was the only one missing the authorization filter.
Impact
- 1,352 prescription records from 86 members returned to any authenticated user
- PHI exposed: medication names, dosages, NDC codes, pharmacy locations, provider IDs
- Patient re-identification possible via member ID + pharmacy location + medication list
- All records returned in a single response with no pagination or rate limiting
Timeline
Reported and triaged within a day. The team validated the authorization bypass and sent it to engineering for remediation.
Takeaway
Always test list endpoints separately from detail endpoints. A platform can have proper 403 checks on profile access and individual record retrieval but completely miss the filter on a bulk listing endpoint. In healthcare, a single unscoped GET returning PHI is a HIPAA incident waiting to happen. The fix is simple: scope the database query to the authenticated user's member ID.