Whisper · Docs
Health

The device/API-abuse cure

A valid-looking FHIR API caller, or a device sitting on the clinical network, is trusted because it is reachable, not because it authenticated. That is the whole abuse. It has an OWASP name, BOLA, because the token authenticates a claim, never the machine on the other end.

Whisper closes it by making the address be the device: a routable IPv6 /128 derived from the key the endpoint or device already holds, DNSSEC-anchored and DANE-pinned, that no caller can forge and one call can revoke worldwide. Unlike the certificate binding UDAP asserts inside a private trust community, it is publicly verifiable across the HDO / vendor / HIE boundary. A stolen token or over-broad scope with no device key behind it authenticates to nothing. This page walks through the abuse end-to-end, the reframe at the reachability layer, the exact live calls (keyless to verify, one keyed call to provision), and, candidly, what identity does and does not cure.

The abuse, at class level

It is not a breach. Your FHIR API and your clinical network are used exactly as they were built, at scale, by a caller no one authenticated.

The mechanics repeat across the sector. A third party reverse-engineers an mHealth or clinician app, increasingly with an LLM's help, until it holds the exact calls the official app makes, then authenticates the same ways the app does: a phished login, a hardcoded key lifted from the app bundle (found in roughly three-quarters of one 30-app set), an over-broad SMART-on-FHIR scope, or an OAuth bearer token portable to any IP. From there it is BOLA: increment the patient id and read the next record, an implementation flaw HL7 has publicly and correctly distinguished from the FHIR standard, yet one found in every production FHIR API in the small set one researcher tested at DEF CON. On the device side the story is quieter and older: land on the flat, converged clinical VLAN, where HL7v2 feeds, unauthenticated DICOM, and proprietary device protocols assume anything on the LAN is trusted. Internet scans keep finding thousands of open DICOM servers that answer without AE-Title validation, exposing well over a billion images.

the device / API-abuse chain: no zero-day required 01 · RECON Find the FHIR API + the clinical net 02 · STEAL A valid credential scope · key · token 03 · BOLA Increment the patient id → the next record 04 · VLAN Or land on the flat VLAN unauth DICOM/HL7v2 05 · ROTATE Hop clouds + residential last IP disposable 06 · REACH On to the next HDO one cred, reusable Two invariants make it invisible at the network layer: ① a real integration is one endpoint an org owns; the abuser is one caller trusted only by reachability. ② every IP it shows you is disposable. The caller that mattered was a third party from the start.
No exploit chain: a valid credential (or an open port on the flat VLAN) used as designed, at scale. The chain leans on exactly two invariants, and neither is an IP you can block.

The root cause is one sentence: reachability is treated as trust. The token, key, or scope authenticates a claim ("I am an authorized caller"), never which machine is on the other end (OWASP broken authentication / BOLA, API1:2023); and the clinical network trusts a device because it can be reached on a segment, not because it proved who it is. So a stolen secret is indistinguishable from the real one, and the source IP that might have narrowed it down is disposable.

Two facts hide it at the network layer. A real integration is one endpoint an org owns; the abuser is one caller trusted only by reachability, holding a valid credential. And the egress hops across clouds or a residential-proxy swarm every few requests, so a security operations center correlates a fresh, meaningless last IP and nothing else. Increasingly the caller that matters is not even on your network: more than 40% of 2024 healthcare breaches began with a third-party vendor, and a credential burned at one organization is reusable against the next, because nothing revokes it across the boundary. That is why an incident here is now a measurable patient-safety event, not only a data one.

The reframe: the address is the device

Detection will always be a step behind a credential that is genuinely valid. You can tune models forever and the abuser still looks exactly like a trusted integration: to your endpoint, it is one. The strictly-stronger move is to change what the exchange trusts, at the layer where the abuse actually lives: reachability.

Whisper has one primitive: the address is the identity. A routable IPv6 /128 out of 2a04:2a01::/32 (announced by AS219419), deterministically derived from a key, DNSSEC-signed to the IANA root, DANE-EE pinned (3 1 1), and RDAP-registered. Anyone can re-derive and verify it with dig.

Point it at the endpoint and the device. Whisper derives the /128 for each FHIR endpoint (or each infusion pump, monitor, or PACS node) from the public key behind the identifier it already carries: the FHIR Endpoint.identifier and its UDAP server certificate, the FDA UDI (the Device Identifier registered in GUDID), a DICOM AE-Title's PS3.15 TLS certificate, an ISO/IEEE 11073 EUI-64, or a TPM / secure element. The Endpoint.identifier or UDI serves as the domain separator. The private key never leaves the device; the address is a one-way function of its public half and that identifier, and the derivation is tenant-bound: the same key and identifier always yield the same /128, and the mapping is unlinkable across organizations to an outsider. The exchange then authorizes on the endpoint's pinned identity, not a stealable token: a request either proves it is the endpoint it claims to be, before a single detection rule runs, or it has no authority at all.

The Endpoint.identifier / UDI is the public index: the /128 is its cryptographic counterpart. GUDID and the TEFCA RCE / NDH endpoint directory publish these identifiers on purpose; they are not secrets, which is exactly what the attacker weaponizes. But the /128 derives from the in-device key domain-separated by that identifier, so the identifier alone yields nothing. You cannot go identifier → /128 without the key, there is no enumerable directory, and RDAP / reverse DNS return the registry object, never the device's whereabouts. (An elegant fit: a point-of-care device's ISO/IEEE 11073 EUI-64 is already the exact 64-bit shape the interface half of an IPv6 address takes under RFC 4291. It's a synthesized angle, but the identity the device was born with can sit on the wire natively.)

reachability = trust, today: identity makes reachability itself provable and revocable Trusted by reachability valid-looking FHIR caller stolen key · over-broad scope a device on the flat VLAN a stolen secret IS the endpoint must prove /128 Each device / endpoint = a /128 from its own key + Endpoint.identifier / UDI DNSSEC · DANE-EE 3 1 1 tenant-bound · forge-proof reach + speak Verifiable, revocable reachability who may reach whom = a query verifiable across HDO · vendor · HIE boundary op:revoke → gone at DNS-TTL the address IS the device: the abuse is cured at the reachability layer, not caught after it
Today a caller or device is trusted for being reachable. Whisper makes each device and endpoint a forge-proof /128, so who may reach and speak to it becomes a verifiable, revocable question: answered at the IP / DNS / transport boundary, publicly, before a detection rule runs.

The highest-leverage move for the API side is turning UDAP's private URI↔SAN assertion into a public one. UDAP's rule is exact and good: the identifying URI SHALL equal the server's {baseURL} and match a uniformResourceIdentifier in the certificate SAN, and /.well-known/udap returns signed_metadata (a JWS carrying the certificate chain in x5c). But that binding is rooted in a private community anchor (a TEFCA or state-HIE CA), so a relying party outside the community has nothing to check, and nothing anchors it in DNS. DANE (RFC 6698 TLSA) plus DNSSEC publish exactly that base-URL↔certificate binding at the IANA root, so any party verifies it with nothing pre-provisioned, and one dropped TLSA revokes it at DNS-TTL. It complements the community CA; it never replaces it.

private, members only UDAP endpoint trust baseURL == cert SAN URI signed_metadata (JWS x5c) anchored in a community CA (TEFCA / state-HIE) outsiders can't check it publish in DNS DNSSEC + DANE TLSA 3 1 1 pin of the same cert base-URL↔cert binding, publicly rooted at the IANA DNS root Any relying party verifies, no anchor, no community membership drop TLSA → revoked, publicly the binding UDAP asserts privately, published for everyone (including outside the community) to check
DANE + DNSSEC publish exactly the base-URL↔certificate binding UDAP asserts inside a private community anchor: any party, even one outside the community, can verify a FHIR endpoint, and a single dropped TLSA revokes it publicly at DNS-TTL. Complements the community CA; never replaces it. See FHIR · UDAP · TEFCA · UDI.

What changes

Nothing here is a new detection rule. Each row is an abuse technique that stops being possible, not one you catch after the fact.

The abuse today Why it dies under identity
Stolen static credential, hardcoded key, or over-broad SMART scope The credential has no device key behind it. State-changing FHIR exchanges terminate mutually-authenticated to the target endpoint's /128 (the endpoint co-signs), so a valid-looking token that can't prove the identity never had authority. BOLA / IDOR lose their leverage: reaching any account no longer reaches any endpoint.
One caller → a whole endpoint directory You cannot present thousands of endpoint / device identities whose keys you do not hold. Every forgery is a DNSSEC / DANE inconsistency any verifier catches with stock tools: dig -x names an identity whose AAAA and TLSA don't agree.
Rotate egress across clouds / residential proxies Identity is not the source IP. The last IP was never the credential, so rotating it (across AWS, GCP, Azure, or a proxy swarm) changes nothing about whether the caller can prove the endpoint.
Reuse a credential across organizations (burned at HDO A, replayed at HDO B) The identity is publicly verifiable across the HDO / vendor / HIE boundary (no shared community anchor required), and one revoke tears down the /128, its PTR, and its DANE pin worldwide at DNS-TTL. The cross-organization kill-switch an in-community CRL / OCSP never gave you.
Blast radius on compromise Compromise one endpoint and you have compromised that endpoint, not the directory: no fleet-wide credential reset, no CRL you hope every device fetched.

Provision a device or endpoint identity

Provisioning is one control-plane call to POST https://graph.whisper.security/api/query with your X-API-Key header. You pass the endpoint's or device's public key material (the base64 SubjectPublicKeyInfo of its UDAP server / IDevID / TPM / secure-element key) and the identifier it already carries as device_id: for a FHIR endpoint, its UDAP identifying URI (equal to the {baseURL} that the server certificate's SAN uniformResourceIdentifier asserts); for a device, its FDA UDI-DI or DICOM AE-Title. You get back the deterministic /128 and a WireGuard config to source that endpoint's traffic from its own address.

Shipped & live. Deriving a device or FHIR-endpoint /128 from its public key plus a generic device_id is in production today. Provision one with the call below and verify it from the DNSSEC root with tools already on your machine. Pass your Endpoint.identifier or FDA UDI as device_id; a first-class typed --udi argument is on the roadmap.

The call

CALL whisper.agents({op:'connect', args:{
  tier:'wireguard',
  identity_public_key:'<base64 SPKI of the endpoint or device key>',   # its UDAP server / IDevID / TPM / secure-element public key
  device_id:'https://fhir.example-hdo.org/r4'                    # the UDAP identifying URI = the FHIR {baseURL} the cert SAN asserts (or an FDA UDI-DI / DICOM AE-Title)
}}) YIELD op, ok, status, result, error
  RETURN op, ok, status, result, error

Over stock tools. jq builds the JSON body so the Cypher's own quotes never fight the shell:

# the public key only, the private key never leaves the device's secure element
Q="CALL whisper.agents({op:'connect', args:{tier:'wireguard', \
   identity_public_key:'MFkwEwYHKoZIzj0…SPKI', device_id:'https://fhir.example-hdo.org/r4'}}) \
   YIELD op, ok, status, result, error RETURN op, ok, status, result, error"

curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  --data "$(jq -nc --arg q "$Q" '{query:$q}')"

The response

# result carries the deterministic identity plus a ready-to-apply tunnel
address        2a04:2a01:f0::fda
fqdn           endpoint-3f2504e0.fhir.<tenant>.agents.whisper.online
ptr            endpoint-3f2504e0.fhir.<tenant>.agents.whisper.online
state          active                       # DNSSEC + DANE-EE (3 1 1) live at provision time
wireguard_config   [Interface] …            # source the endpoint's traffic from its own /128

The endpoint now has a name it can prove and an address it egresses from. Reverse DNS resolves the /128 to that identity, a TLSA record pins the leaf key (the very certificate your UDAP {baseURL} SAN asserts), and RDAP registers the object under 2a04:2a01::/32, the full seven-proof chain, published atomically with the allocation.

Idempotency and errors

The call is deterministic and honest about conflicts: conservative in what it emits, liberal in what it accepts.

You send You get
The same key + device_id again The same /128: idempotent, safe to retry, safe to run on every boot. No duplicate identities.
The same key with a different device_id on your tenant 409 Conflict: a key binds to exactly one identifier. The error.detail names the device_id it is already bound to.
A non-string device_id (a number, an array, null) 400 with an actionable detail, never an opaque 500. Send the identifier as a string.

On the CLI: whisper create --register mints a generic identity, and whisper verify / whisper policy / whisper logs / whisper kill --revoke drive the rest. The health-specific --udi flag is not shipped yet. Provision endpoints and devices through the control-plane call above, which is live today, and drive them with the CLI verbs once allocated.

Revoke, worldwide

Decommission, a board swap, a transfer between organizations, or a compromised device is one call. It is provable with the same stock tools that proved the identity existed, no Whisper software required.

# the control-plane op…
CALL whisper.agents({op:'revoke', args:{agent:'2a04:2a01:f0::fda'}})
# …or the CLI
whisper kill --revoke 2a04:2a01:f0::fda

# now prove it, worldwide, at DNS-TTL speed:
dig -x 2a04:2a01:f0::fda +short                        # -> nothing
curl -s https://whisper.online/verify-identity/2a04:2a01:f0::fda
# -> {"is_whisper_agent": false, ...}

Verify it: keyless, no account

The identity is public by design, so anyone (a partner HIE, a QHIN onboarding you, an OCR auditor, a device PSIRT) can check an endpoint without your key and without taking Whisper's word for it. This is the keyless half of the two-tier surface: verify with no key, provision and govern with your key.

# no key, no account: re-derive and verify the endpoint's identity, trustless to the IANA root
whisper verify --trustless 2a04:2a01:f0::fda

# or with only curl: the keyless full-chain verdict
curl -s https://whisper.online/verify-identity/2a04:2a01:f0::fda
# { "is_whisper_agent": true, "dane_ok": true, "jws_ok": true, "evidence": { ... } }

# the address IS the endpoint: forward-confirmed reverse DNS names it
dig -x 2a04:2a01:f0::fda +short
# endpoint-3f2504e0.fhir.example-hdo.whisper.online.

# the registry object for the /128: RDAP, typed JSON
curl -s https://whisper.online/ip/2a04:2a01:f0::fda | jq '.handle, .parentHandle'
# "2A04:2A01:F0::FDA/128"
# "2A04:2A01::/32"

The --trustless flag is the point: nothing there calls back to Whisper's own API as an authority. The CLI re-derives the DNSSEC chain to the IANA root, on your machine, with your resolver. A partner outside your trust community can verify a FHIR endpoint without joining it. Full mechanics: Verify an agent and DANE & TLSA.

Name what already got in

Identity stops the next forgery. It does not name the operator behind the sessions already in your logs. The same platform back-traces them, and the attribution survives the rotation because it fingerprints the operator's infrastructure and tooling, not the ephemeral egress IP. That is the piece an in-hospital sensor structurally can't reach: it stops at your edge, while the caller that mattered was a third party from the start.

what your SOC / API gateway sees: a rotating, meaningless “last IP” Suspect sessions from your gateway logs stolen token · scope AWS eu-central 3.68.x.x GCP europe-w4 34.90.x.x Azure westeu 20.61.x.x residential-proxy swarm 71.x · Comcast 82.x · KPN 99.x · Orange infra genealogy JA4 fingerprint One operator ASN + hosting genealogy + JA4 / JA3 fingerprint evidence chain → your SIEM what the graph sees: one operator
Attribution survives rotation because it tracks the operator's infrastructure and tooling, not the ephemeral egress IP. The JA4 fingerprint lives in the TLS handshake the proxy can't rewrite. Express “one source touching N distinct device / endpoint identities across two HDOs in a window” as a query, not a ticket.

Take a suspect egress IP straight from your SOC or API-gateway logs and ask the graph who really operates it. This runs read-only over the same public graph API, with your key:

# who really operates a host, even behind a CDN or a cloud front
curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" -H "content-type: application/json" \
  -d '{"query":"CALL whisper.identify(\"203.0.113.10\")"}'

The read-only verbs (identify, origins, walk, variants, history) run over that one endpoint against a live internet-infrastructure graph of fused BGP, DNS, WHOIS, TLS, hosting, and threat intelligence. Cloud rotation collapses through origins and walk, which cluster shared ASN, hosting, and certificate lineage into one infrastructure genealogy; a residential-proxy swarm collapses through a JA4 / JA3 client fingerprint that travels with the tooling, invisible to your API gateway because it lives in the TLS handshake; history gives a timeline over a suspect operator. Every answer is reproducible, replayable JSON: the paper trail an OCR or HIPAA finding needs, not a screenshot. Express "one source touching N distinct device / endpoint identities across two organizations in a window" as a query, not a ticket.

These graph verbs are the API surface: you call the endpoint directly, as above. There is no whisper identify / graph / export CLI subcommand; the CLI covers the control plane (create, verify, policy, logs, kill), and the graph is the query API. See Graph & cognition.

See who's reaching it and govern what it reaches

An identity you can prove is also one you can watch and constrain. Because every device and endpoint resolves through Whisper's own authoritative DNS and RDAP, the owner sees exactly who looked, governs precisely what each device may talk to, and keeps an auditable record of every issuance: three doors a private community directory never opened.

The same address-is-identity primitive that governs a compromised infusion pump also governs the AI agents your clinical and revenue-cycle teams are about to run against the EHR: per-agent /128, per-agent logs, default-deny egress, one revoke. From day one. See Egress governance.

Honest scope: what it cures, and what it doesn't

This is device- and endpoint-identity at the IP / DNS / transport boundary. It is additive, and it is candid about its edges: over-claiming fails a QHIN architect's review, and it should.

What it cures. Possession of a stolen secret stops being sufficient: a hardcoded key, a long-lived token, or an over-broad SMART scope with no device key behind it authenticates to nothing. UDAP's private base-URL↔certificate binding becomes publicly verifiable, shrinking token-replay and rogue-aggregator risk across the community boundary. Attribution survives egress and IP rotation and reaches across organizations. Revocation is cross-organization, at DNS-TTL. Egress governance constrains who a device may reach and be reached by, cutting C2 and exfil paths. And the per-/128, UDI-keyed identity plus the attribution graph are the asset-identity and network-map evidence FDA §524B and the HIPAA Security Rule NPRM ask for.

What it plainly does not cure.

Additive, never a replacement. It complements, never replaces, UDAP / SMART / TEFCA trust, OAuth, your OEM's build-time device PKI, your network segmentation, and your device patching and lifecycle. It rides on top of the anchors you already ship, and it is built to fail open: a Whisper outage never bricks a device, checks degrade to the anchors you already run, and connectivity is preserved.

Where it fits: standards, SIEM, integrations

Compliance. The forge-proof, UDI-keyed per-device identity is a canonical asset-inventory anchor and its attribution graph is a live network map: direct evidence for three of the HIPAA Security Rule NPRM's hardest asks (asset inventory, network map, segmentation), plus entity authentication and an egress audit trail under §164.312(a)/(b)/(d). For a device maker, it is a built-in authentication and unauthorised-access control to point to in an FDA §524B(b)(2) security architecture, with demonstrable postmarket containment (a stable per-device identity and one-call revocation) for §524B(b)(1) and coordinated vulnerability disclosure, keyed to the UDI already in labelling and GUDID. It also maps to EU MDR Annex I §17.4 and IEC 62443 FR1. The clause-by-clause mapping lives in FDA 524B · HIPAA · MDR.

Shipped vs roadmap. The Splunk, Microsoft Sentinel and OpenCTI connectors ship today; findings arrive as signed JSON mapped to CEF and ECS fields. Roadmap, labelled as such and not yet available: STIX 2.1 over TAXII export, a machine-readable Health-ISAC peer-sharing export, and the first-class typed --udi control-plane argument.

Integrations (proposed, not vendor-endorsed). Whisper anchors the cloud, API, and IP boundary, never the in-hospital clinical bus, the device's on-box command path, or a legacy protocol authenticator:

Next