Healthcare runs on APIs now. Every patient portal, telehealth app, care coordination platform, and analytics pipeline talks to an EHR through HL7 v2 interfaces or FHIR APIs. These interfaces carry the most sensitive data a person has, yet they are often the least tested part of a healthcare environment. In engagement after engagement, Redfox Cybersecurity finds the same thing: a large share of healthcare APIs will expose protected health information (PHI) to someone who should not see it, usually through authorization flaws rather than exotic exploits.
This article is written for the developers and security engineers who build and defend these interfaces. It walks through the specific weaknesses we test for during FHIR API penetration testing and HL7 security testing, with sanitized request examples and the kind of Burp Suite workflow we actually use. Everything here is framed for defenders testing their own systems with authorization. The goal is to help you find these issues in your own environment before someone else does.
Good, deeply technical material on this subject is scarce, which is unfortunate given how much PHI flows through these interfaces every second. Developers who inherit a FHIR facade or an HL7 interface engine are often handed a working integration and a deadline, with little guidance on how to break it safely. The sections below are meant to close that gap: a practitioner's reference you can work through endpoint by endpoint.
The root cause is rarely a missing patch. It is almost always broken authorization. HL7 v2 was designed for trusted hospital networks where every system on the wire was assumed friendly. FHIR is modern and secure by design, but it is frequently implemented in a hurry, with authorization logic bolted on by teams racing to meet an interoperability deadline or a regulatory mandate.
None of these require advanced skills to exploit, which is exactly why they matter. If your interfaces have never been tested with multiple patient identities and manipulated tokens, a healthcare penetration testing engagement focused on API authorization is the fastest way to find out where you stand.
Before any testing, confirm you have written authorization to test the target, and prefer a staging environment seeded with synthetic data over anything containing real PHI. For FHIR work, we configure Burp Suite with two authenticated sessions belonging to two different test patients, which makes authorization flaws obvious the moment one patient can read the other's data.
The principle throughout is minimal proof. When a request returns another patient's data, a single record and a count are enough to demonstrate the issue. There is never a reason to bulk-extract PHI to prove a point.
This is the single most common critical finding in FHIR implementations. The server authenticates the caller but fails to confirm that the requested resource belongs to that caller.
A normal, authorized read for the logged-in patient looks like this:
GET /fhir/r4/Patient/1001 HTTP/1.1
Host: api.example-health.test
Authorization: Bearer <patient-A-token>
Accept: application/fhir+json
[cta]
The test is to keep Patient A's token and request Patient B's resource:
GET /fhir/r4/Patient/1002 HTTP/1.1
Host: api.example-health.test
Authorization: Bearer <patient-A-token>
Accept: application/fhir+json
If the second request returns Patient B's demographics instead of an authorization error, the API is not enforcing object-level authorization. The same test applies to clinical resources, where the impact is often higher:
GET /fhir/r4/Observation?patient=1002 HTTP/1.1
Host: api.example-health.test
Authorization: Bearer <patient-A-token>
Accept: application/fhir+json
[cta]
A correct implementation binds every query to the patient compartment in the token and ignores or rejects a conflicting patient parameter. If you confirm this behavior in your own environment, treat it as critical and prioritize the fix. Our team validates exactly these compartment and context controls during HIPAA penetration testing, mapping each finding to the Security Rule access control safeguard.
SMART on FHIR layers OAuth 2.0 and OpenID Connect on top of FHIR. When it is implemented correctly, it is strong. The problems come from incomplete token validation and loose scoping.
Decode the access token (never alter tokens outside a system you are authorized to test) and review its claims. A typical SMART token carries something like:
{
"iss": "https://auth.example-health.test",
"aud": "https://api.example-health.test/fhir",
"sub": "patient-A",
"patient": "1001",
"scope": "patient/Observation.read patient/Patient.read",
"exp": 1893456000
}
[cta]
The defensive tests mirror the claims. On a system you are authorized to assess, confirm that the API:
aud value pointing at a different application or API.patient claim on every query rather than trusting a request parameter.Request a narrowly scoped token, for example patient/Observation.read, then confirm the API refuses a read of a resource outside that scope:
GET /fhir/r4/MedicationRequest?patient=1001 HTTP/1.1
Host: api.example-health.test
Authorization: Bearer <observation-only-token>
Accept: application/fhir+json
[cta]
If a token scoped only to Observations returns MedicationRequests, scope enforcement is incomplete. A frequent variant is an API that enforces scope on the primary resource but not on resources pulled in through _include or _revinclude, so always test those too. If you want an independent team to run this full token and scope matrix against your SMART on FHIR app, our healthcare penetration testing services cover the entire authorization surface.
The FHIR Bulk Data $export operation is powerful and therefore high risk. It is designed for backend services pulling population-level data, so a flaw here can expose records for an entire patient group rather than a single person.
A system-level export kickoff looks like this:
GET /fhir/r4/$export HTTP/1.1
Host: api.example-health.test
Authorization: Bearer <backend-service-token>
Accept: application/fhir+json
Prefer: respond-async
[cta]
When assessing your own export implementation, verify the following controls:
Group/[id]/$export, confirm a client can only export groups it is explicitly authorized for, not any group ID it supplies.The most damaging export finding is usually the output files, not the kickoff. A correctly authorized export that writes results to an endpoint anyone can fetch turns a legitimate feature into a bulk PHI leak. After remediation, every one of these controls should be retested.
HL7 v2 remains the backbone of hospital integration, and it carries assumptions from an era of trusted networks. These assumptions are where the risk lives, and they are precisely what HL7 security testing has to probe. Unlike FHIR, HL7 v2 has no native concept of authentication or authorization, so the controls that protect it sit in the network and the interface engine around it. That makes the testing as much about architecture as about the messages themselves.
HL7 v2 messages typically travel over the Minimal Lower Layer Protocol on a raw TCP socket. Without TLS, the entire message, including patient identifiers and clinical data, crosses the network in cleartext. On a network segment you are authorized to assess, passive capture will quickly show whether messages are readable. A sanitized ADT message looks like this, with delimiters shown and values replaced:
MSH|^~\&|SENDING_APP|SENDING_FAC|RECV_APP|RECV_FAC|20260101120000||ADT^A01|MSG00001|P|2.5
PID|1||MRN12345^^^HOSP^MR||DOE^JANE||19700101|F|||123 MAIN ST^^CITY^ST^00000
Many HL7 listeners accept any well-formed message from any source that can reach the port. The defensive test, on an authorized interface engine, is to confirm whether the listener authenticates senders at all, and whether it is restricted to known source addresses. If it accepts a benign, well-formed test message from an unexpected host, sender trust is too loose. The remediation is network segmentation, allowlisting interface engine addresses, and mutual TLS.
HL7 parsers must handle field, component, and subcomponent delimiters, escape sequences, and optional segments. A common defensive test is to send structurally malformed or oversized messages to a test listener and observe whether it crashes, hangs, or misinterprets data. Values that contain delimiter characters are a particular concern, because a field value carrying an extra field separator can shift how later fields are read. The fix is strict validation that rejects malformed messages rather than trying to repair them, plus fuzzing the parser as part of regular testing.
Because every one of these HL7 weaknesses exposes PHI, they are HIPAA findings as much as technical ones. We map HL7 transport, authentication, and parsing issues to the relevant Security Rule safeguards during HIPAA penetration testing so compliance and engineering teams work from one report.
The findings above come from a small set of root causes, and the fixes are well understood.
patient parameter conflicts with the token context.$export to appropriately privileged backend clients using asymmetric keys.For every control above, confirm that access to PHI, including failed and denied attempts, is logged with user identity, patient context, and timestamp. During an incident, missing logs make it impossible to scope what happened. A denied request that leaves no trace is nearly as dangerous as an allowed one, because it hides reconnaissance and failed exploitation from your detection team.
Not every finding is equal. A scope-enforcement gap on a read-only Observation endpoint is serious, but an authorization flaw on a write operation that can alter a MedicationRequest or a bulk export that exposes an entire group is more serious still. We score findings on both exploitability and the volume and sensitivity of the PHI they touch, so remediation effort goes where patient impact is highest rather than where a scanner happened to assign a generic severity.
A point-in-time test is valuable, but healthcare APIs change constantly. The teams that stay ahead build authorization testing into their development process.
Independent testing matters because the most serious findings come from chaining issues that each look minor in isolation, which is difficult to encode in a unit test. Redfox Cybersecurity's healthcare penetration testing team focuses on exactly those attack paths across HL7 and FHIR interfaces.
$export operation needs special care, and its output files are the most common weak point.Healthcare APIs are where the data lives, and attackers know it. If you build or defend HL7 and FHIR interfaces, the practical next step is to test them the way an attacker would, with authorization and in a safe environment. Talk to Redfox Cybersecurity about FHIR and HL7 API penetration testing, or explore our HIPAA penetration testing services to validate the interfaces carrying your patients' data.