← All write-ups
October 6, 2026CRITICALSANITIZED CLIENT FINDING

Cross-domain IDOR: mass disclosure of user and HR records

2026 · Client A · Healthtech & medical devices

Summary

Several authenticated API resources trusted client-controlled identifiers and domain parameters. Changing those values exposed records belonging to other users and organizational domains.

Technical workflow

The authorization matrix used synthetic records and two approved test identities. A single-object request was enough to validate the server decision; no bulk enumeration was required:

curl --fail-with-body --max-time 5 \
  -H 'Authorization: Bearer TEST_TOKEN_A' \
  'https://AUTHORIZED_HOST/api/records/TEST_RECORD_B?domain=TEST_DOMAIN_B'

The expected response for an object outside the caller’s scope is a uniform 403 or 404. A minimal policy boundary looks like this:

def can_read(principal, record) -> bool:
    return (
        principal.is_authenticated
        and principal.tenant_id == record.tenant_id
        and record.id in principal.visible_record_ids
    )

def require_read(principal, record) -> None:
    if not can_read(principal, record):
        raise PermissionError("record is not visible to this principal")

The same decision must protect list filters, individual reads, exports and downloads. Pagination and rate limiting reduce blast radius but do not repair broken object authorization.

Impact

The same authorization gap affected listings, individual records and document metadata. A low-privilege account could therefore move from a single-object test to large-scale data exposure.

Defensive lessons

Retest evidence should include response status, response shape, audit event and the absence of sensitive fields for every denied combination.

The affected organization and exact identifiers are withheld.