Date
August 11, 2026
Author
Karan Patel
,
CEO

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.

Why Healthcare APIs Leak PHI So Often

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.

The usual suspects

  • Object-level authorization gaps: the API checks that you are logged in but not that the specific record belongs to you. This is OWASP API1, Broken Object Level Authorization, the API term for IDOR.
  • Token validation weaknesses: signatures, audience, and expiry are not fully checked, or the patient context in the token is not enforced on the query.
  • Transport and protocol assumptions: HL7 v2 over cleartext MLLP, listeners that trust any sender, and parsers that mishandle malformed input.
  • Over-broad scopes: SMART on FHIR scopes granted far wider than the app needs, so a single compromised token exposes everything.

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.

Setting Up to Test a FHIR API in Burp Suite

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.

A practical Burp workflow

  • Proxy the client app or script through Burp and capture a normal, authorized request for Patient A.
  • Send it to Repeater, then systematically change identifiers, parameters, and token fields.
  • Use the Comparer to diff responses between Patient A and Patient B.
  • Use Intruder with a short, controlled list of identifiers to confirm, not mass-harvest, that enumeration is possible.

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.

FHIR Broken Object Level Authorization (IDOR)

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 Authorization Weaknesses

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.

Token claims worth inspecting

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:

  • Rejects tokens with an aud value pointing at a different application or API.
  • Rejects unsigned tokens and tokens using an unexpected signature algorithm.
  • Rejects expired tokens and does not accept a refresh token where an access token is required.
  • Enforces the patient claim on every query rather than trusting a request parameter.

Scope enforcement

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.

Testing the FHIR Bulk Data $export Operation

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:

  • Authorization tier: a patient-scoped or user-scoped token must not be able to launch a system-level or group-level export. Confirm the server rejects it.
  • Group restriction: for Group/[id]/$export, confirm a client can only export groups it is explicitly authorized for, not any group ID it supplies.
  • Output file protection: the status response returns URLs to the generated files. Confirm those URLs require authentication and are not long-lived, unauthenticated links. We regularly find export files sitting in cloud storage behind pre-signed URLs that stay valid far too long.
  • Backend auth strength: the SMART Backend Services flow should use asymmetric keys (a signed client assertion), not a shared secret.

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 Security Testing: What to Check in v2 Interfaces

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.

Cleartext MLLP

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:

Segment Field Value
MSH Sending Application SENDING_APP
MSH Sending Facility SENDING_FAC
MSH Receiving Application RECV_APP
MSH Receiving Facility RECV_FAC
MSH Message Date/Time 20260101120000
MSH Message Type ADT^A01 (Admit / Visit Notification)
MSH Message Control ID MSG00001
MSH Processing ID P (Production)
MSH HL7 Version 2.5
PID Patient MRN MRN12345 (assigning authority: HOSP, type: MR)
PID Patient Name DOE^JANE (Last^First)
PID Date of Birth 19700101
PID Administrative Sex F (Female)
PID Patient Address 123 MAIN ST^^CITY^ST^00000

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

Message injection and sender trust

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.

Parser robustness and delimiter handling

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.

How to Fix the Most Common HL7 and FHIR API Flaws

The findings above come from a small set of root causes, and the fixes are well understood.

Enforce authorization on every object

  • Bind every FHIR query to the patient compartment in the token.
  • Reject requests where a patient parameter conflicts with the token context.
  • Apply the same check to bundles, includes, and references, not just the top-level resource.

Validate tokens completely

  • Verify signature, algorithm, issuer, audience, and expiry on every request.
  • Reject unsigned tokens and tokens issued for another application.
  • Enforce SMART scopes at the resource and interaction level, including on included resources.

Protect bulk data

  • Restrict $export to appropriately privileged backend clients using asymmetric keys.
  • Authorize group exports against an allowlist, not a client-supplied ID.
  • Require authentication on output files and keep their URLs short-lived.

Secure HL7 transport and parsing

  • Wrap MLLP in TLS with mutual authentication, or tunnel it over an authenticated VPN.
  • Restrict listeners to known interface engine addresses and segment the clinical network.
  • Validate strictly, reject malformed messages, and fuzz parsers regularly.

Verify your logging

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.

Prioritize by patient-safety and PHI impact

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.

Building API Security Testing Into Your Pipeline

A point-in-time test is valuable, but healthcare APIs change constantly. The teams that stay ahead build authorization testing into their development process.

  • Write automated tests that assert Patient A cannot read Patient B's resources, and run them on every build.
  • Add token validation tests that confirm expired, unsigned, and wrong-audience tokens are rejected.
  • Include FHIR interface tests after every change to search parameters, includes, or scopes.
  • Pair that continuous coverage with an independent manual assessment on a regular cadence, since automated tests only catch what you thought to write.

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.

Key Takeaways

  • Most healthcare API PHI exposure comes from broken authorization, not missing patches.
  • FHIR IDOR is the most common critical finding: test every endpoint with two patient identities.
  • SMART on FHIR is strong when token audience, signature, expiry, and scopes are all enforced, including on included resources.
  • The FHIR $export operation needs special care, and its output files are the most common weak point.
  • HL7 v2 carries cleartext and trusted-network assumptions that must be fixed with TLS, segmentation, sender authentication, and strict parsing.
  • Test your own systems with authorization, prove findings with minimal evidence, and build authorization tests into your pipeline.

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.

Copy Code