Skip to main content

Property Filters

Property filters modify or remove specific properties from FHIR resources before they are returned to the client. They are applied after authorization succeeds, allowing you to grant access to a resource while redacting sensitive fields.

Property filtering with search access requires careful configuration

Property filters are a powerful tool for in-place anonymization, but they are not trivial to configure securely when the client also has search access.

Property filters only redact responses. The underlying data remains fully indexed and searchable. A determined client can exploit this to reverse-engineer redacted values through targeted searches, _sort ordering, _include directives, _filter expressions, chained search parameters, or other FHIR query mechanisms. Each of these vectors must be explicitly blocked — there is no single switch that closes all side-channels at once.

The safest approach is to grant only read access (not search) to clients that receive property-filtered data. When a client can only read resources by ID, there is no search surface to exploit. Property filters on read operations are straightforward and secure without additional configuration.

If you must allow search access alongside property filters, read the Preventing Search-Based Data Leakage section carefully and understand that missing a single search parameter from the blocked list can allow a client to circumvent anonymization entirely.

GraphQL and HFQL offer a more controlled surface than REST search:

  • GraphQL read (graphql-read) with inline reference expansion is safe: references are resolved by ID (not by client-controlled search arguments), and property filters apply to all expanded resources automatically. This is the recommended access pattern for clients that need to traverse resource graphs while property filters are active.
  • HFQL is fail-closed: when blocked-search-params is configured for a resource type, all HFQL queries on that type are rejected outright. There is no partial leakage path.
  • GraphQL search (graphql-search) has the same risks as REST search — search arguments map to FHIR search parameters and must be blocked in the same way.

As a rule of thumb: prefer read + GraphQL read over search for property-filtered roles. If search is required, budget time for a thorough audit of every search parameter that could reveal redacted data.

Use Cases

  • Clinical studies - Provide access to clinical data while hiding patient identifying information. See the Automatic Anonymization how-to for a complete walkthrough.
  • Privacy regulations - Remove contact details or addresses from resources shared with certain roles
  • Data minimization - Return only the fields a client actually needs

How They Work

Property filters are attached to authorization rules. When a rule with property filters matches and the validator grants access, Fire Arrow processes each filter against the resource before returning it:

Each filter targets a specific property using a FHIRPath expression and applies a transformation to it.

Available Filters

NullFilter

Removes the property entirely from the resource. The field will not appear in the response.

property-filters:
- property: "name"
filter: NullFilter

RandomFilter

Replaces the property with randomly generated data. This preserves the structure of the resource while making the actual values unreadable. RandomFilter supports HumanName and ContactPoint data types.

property-filters:
- property: "name"
filter: RandomFilter

For a HumanName, RandomFilter generates a random given name and family name. For a ContactPoint, it generates a random value appropriate for the contact system (e.g., a random phone number or email).

Configuration

Property filters are defined as part of an authorization rule:

fire-arrow:
authorization:
rules:
- client-role: Practitioner
resource: Patient
operation: read
validator: Allowed
property-filters:
- property: "name"
filter: NullFilter
- property: "telecom"
filter: RandomFilter
- property: "address"
filter: NullFilter

In this example, when a Practitioner reads a Patient:

  • The patient's name is removed entirely
  • The patient's telecom (phone, email) is replaced with random values
  • The patient's address is removed entirely

All other fields (identifiers, clinical links, etc.) are returned normally.

Preventing Search-Based Data Leakage

Security: Property filters alone do not prevent data probing

Property filters redact fields from responses, but the underlying data remains fully indexed and searchable. Without additional configuration, a client can infer redacted values by issuing targeted searches and observing whether results come back. You must configure blocked-search-params and/or blocked-includes on every search rule that co-exists with property filters to close this side-channel.

This configuration is not trivial. FHIR has a large and complex search surface: direct search parameters, chained parameters, _include/_revinclude, _filter expressions, _has reverse chaining, and more. Missing a single vector means a client can circumvent your anonymization. Treat blocked-search-params configuration as a security audit, not a quick checkbox.

The Threat

When a property filter removes a patient's name, a client can still call:

GET /fhir/Patient?name=Smith

If the search returns results, the client knows a patient named "Smith" exists — even though the name was removed from the response. The same applies to any searchable field that is property-filtered.

Even without a direct search filter, a client can use _sort to infer redacted values:

GET /fhir/Patient?_sort=name

The response order reveals the relative ordering of redacted names across patients — grouping identical values and exposing alphabetical rank — even though the names themselves are not shown.

Similarly, if Patient.generalPractitioner is filtered out, a client can use _include to reveal the relationship:

GET /fhir/Patient?_include=Patient:general-practitioner

The included Practitioner resource in the response bundle reveals the filtered reference.

Prefer read-only access for property-filtered roles

The simplest and most reliable way to use property filters is to not grant search access at all. If a client only has read and/or graphql-read permissions, there is no search surface to exploit and property filters are fully effective without any additional configuration.

Only add search rules for property-filtered roles when you have a genuine business requirement and are prepared to maintain a complete and audited blocked-search-params list.

Blocking Search Parameters

The blocked-search-params field on a validation rule lists FHIR search parameter names that must be rejected. When a client uses any of these parameters, the server returns 403 Forbidden.

The mapping from FHIRPath properties to search parameters is many-to-many, so blocked parameters must be listed explicitly. There is no automatic derivation — this is intentional, because the mapping between FHIRPath expressions and FHIR search parameters is complex and context-dependent. An incorrect automatic mapping would create a false sense of security.

FHIRPath propertySearch parameters to block
namename, family, given, phonetic
telecomtelecom, phone, email
addressaddress, address-city, address-state, address-postalcode, address-country
birthDatebirthdate

Consult the FHIR search parameter registry for the resource type you are filtering to identify all search parameters that correspond to the properties you are redacting.

Blocking Includes

The blocked-includes field lists _include and _revinclude directives in ResourceType:searchparam format. These are only needed when property filters target reference fields (since _include follows references).

blocked-includes:
- "Patient:general-practitioner"
- "Patient:link"

What Happens on Violation

When a blocked parameter or include is used, the server returns:

HTTP 403 Forbidden
Search parameter 'name' is not permitted for this operation

The error message does not reveal why the parameter is blocked, to avoid leaking filter configuration details.

_sort, _filter, _has, _text, and _content Parameters

When blocked-search-params is configured, the following parameters are automatically handled:

  • _sort — Each sort key is checked against the blocked list. Sorting by a blocked parameter is rejected because the response order leaks relative values of the redacted field. Multi-key sort values (e.g., _sort=birthdate,-name) are parsed individually; the request is rejected if any key is blocked.
  • _filter, _has, _text, _content — These parameters are rejected entirely on both REST and GraphQL search requests. They support arbitrary or full-text search criteria that could reference any blocked parameter, making fine-grained analysis fragile. The _has reverse-chaining parameter is particularly dangerous because it embeds search parameter names deep inside a colon-delimited chain (e.g., _has:Patient:link:name=Smith) where they are invisible to normal parameter inspection. The server uses a fail-closed approach across all search paths.

GraphQL and HFQL Coverage

GraphQL read and HFQL are safer than REST search

GraphQL read operations (inline reference expansion) are the most secure access pattern for property-filtered data. When a GraphQL query expands a reference (e.g., Observation { subject { name } }), the server resolves the reference by resource ID — not through a client-controlled search. Property filters apply to all resolved resources automatically. The client has no way to inject search criteria into the resolution path, making this side-channel free.

HFQL takes a fail-closed approach: when blocked-search-params is configured for a resource type, all HFQL queries on that type are rejected outright. HFQL WHERE clauses use FHIRPath expressions rather than search parameter names, making fine-grained analysis complex. This fail-safe may be relaxed in a future version.

GraphQL search operations (e.g., PatientList(name: "Smith")) have the same risk profile as REST search. GraphQL search arguments are mapped to FHIR search parameters and checked against blocked-search-params in the same way. The _sort, _filter, _has, _text, and _content parameters are also rejected on GraphQL search when blocked params are configured, mirroring the REST behavior. You need the same blocking configuration for graphql-search rules as for REST search rules.

Summary of access patterns by risk level:

Access patternSide-channel riskAdditional config needed
read (REST)NoneNo
graphql-read (inline expansion)NoneNo
HFQL (with blocked-search-params)None (fail-closed)blocked-search-params triggers rejection
search (REST)High — requires careful blockingblocked-search-params, blocked-includes
graphql-searchHigh — same as REST searchblocked-search-params, blocked-includes

Full Example: Clinical Study Access

Read this section carefully before deploying property filters with search access

The configuration below is provided as a reference. In a production deployment, you should audit every search parameter for the resource types you are filtering and validate that no search vector is left open. Consider starting with read-only access and adding search only if there is a concrete business requirement.

A clinical study needs practitioners to access observation data linked to patients, but patient-identifying information must be hidden:

fire-arrow:
authorization:
rules:
# Study practitioners can read patients but without identifying info
- client-role: Practitioner
resource: Patient
operation: read
validator: OrganizationCompartment
identity-filter: "meta.tag.where(system = 'http://example.org/role' and code = 'study-practitioner').exists()"
property-filters:
- property: "name"
filter: RandomFilter
- property: "telecom"
filter: NullFilter
- property: "address"
filter: NullFilter
- property: "birthDate"
filter: NullFilter

# Study practitioners can search patients — with blocked search params
- client-role: Practitioner
resource: Patient
operation: search
validator: OrganizationCompartment
identity-filter: "meta.tag.where(system = 'http://example.org/role' and code = 'study-practitioner').exists()"
property-filters:
- property: "name"
filter: RandomFilter
- property: "telecom"
filter: NullFilter
- property: "address"
filter: NullFilter
- property: "birthDate"
filter: NullFilter
blocked-search-params:
- "name"
- "family"
- "given"
- "phonetic"
- "telecom"
- "phone"
- "email"
- "address"
- "address-city"
- "address-state"
- "address-postalcode"
- "address-country"
- "birthdate"

# Study practitioners can read observations normally
- client-role: Practitioner
resource: Observation
operation: read
validator: OrganizationCompartment
identity-filter: "meta.tag.where(system = 'http://example.org/role' and code = 'study-practitioner').exists()"

# Regular practitioners see everything
- client-role: Practitioner
resource: Patient
operation: read
validator: PractitionerCompartment

In this setup, study practitioners see patient resources with randomized names and no contact information or birth dates, while regular practitioners with a compartment relationship see the full patient record. Study practitioners cannot probe redacted values through search, _sort, _include, _filter, _has, GraphQL arguments, or HFQL queries.