Filter Strategies
A filter strategy controls what happens to a detected PII token. Each identifier type supports a Strategies list; the first strategy whose condition evaluates to true is applied. If the list is empty, the default REDACT strategy is used.
Available Strategies
| Strategy | Constant | Description |
|---|---|---|
REDACT |
AbstractFilterStrategy.Redact |
Replace the token with a formatted redaction label |
RANDOM_REPLACE |
AbstractFilterStrategy.RandomReplace |
Replace with a realistic, type-appropriate fake value |
STATIC_REPLACE |
AbstractFilterStrategy.StaticReplace |
Replace with a fixed string |
CRYPTO_REPLACE |
AbstractFilterStrategy.CryptoReplace |
Replace with AES-GCM encrypted ciphertext |
FPE_ENCRYPT_REPLACE |
AbstractFilterStrategy.FpeEncryptReplace |
Format-preserving encryption (FF3-1) |
HASH_SHA256_REPLACE |
AbstractFilterStrategy.HashSha256Replace |
Replace with the SHA-256 hex digest |
LAST_4 |
AbstractFilterStrategy.Last4 |
Keep only the last 4 characters |
MASK |
AbstractFilterStrategy.Mask |
Overwrite characters with a mask character |
SAME |
AbstractFilterStrategy.Same |
Leave the token unchanged (mark as detected but not replaced) |
TRUNCATE |
AbstractFilterStrategy.Truncate |
Keep only the first character |
SHIFT_DATE |
AbstractFilterStrategy.ShiftDate |
Shift a detected date by a configurable offset (date filters only) |
Strategy Details
REDACT
Replaces the token with a formatted label. The redactionFormat string may contain:
%t— replaced with the filter type name (e.g.ssn,email-address)%l— replaced with the token's classification label (if any)
Default format: {{{REDACTED-%t}}}
new SsnFilterStrategy
{
Strategy = "REDACT",
RedactionFormat = "[REMOVED-%t]"
}
{ "strategy": "REDACT", "redactionFormat": "[REMOVED-%t]" }
RANDOM_REPLACE
Replaces the token with a realistic, type-appropriate fake value generated by the anonymization service for the filter type (for example, a fake SSN-shaped value for an SSN, a fake date for a date). When no anonymization service is wired in (for strategies constructed directly, outside FilterService), it falls back to a random GUID.
new SsnFilterStrategy
{
Strategy = "RANDOM_REPLACE"
}
Replacement scope — replacementScope controls whether a token's replacement is reused:
replacementScope |
Behaviour |
|---|---|
"DOCUMENT" (default) |
Each occurrence is anonymized independently. |
"CONTEXT" |
A token's replacement is reused across the context, so the same input value always maps to the same fake value (referential integrity). |
new SsnFilterStrategy
{
Strategy = "RANDOM_REPLACE",
ReplacementScope = "CONTEXT" // reuse the same fake value for the same SSN
}
Choosing the fake values — by default RANDOM_REPLACE generates realistic values. You can override this:
| Property | JSON key | Description |
|---|---|---|
anonymizationCandidates |
anonymizationCandidates |
When non-empty, replacements are drawn from this explicit list of values instead of being generated. |
anonymizationMethod |
anonymizationMethod |
The generation method when no candidates are supplied (defaults to realistic generation). |
new FirstNameFilterStrategy
{
Strategy = "RANDOM_REPLACE",
AnonymizationCandidates = new List<string> { "Alex", "Jordan", "Riley" }
}
See Context Service for details on referential integrity with CONTEXT scope.
STATIC_REPLACE
Replaces the token with a fixed string supplied in staticReplacement. Falls back to REDACT format if staticReplacement is empty.
new EmailAddressFilterStrategy
{
Strategy = "STATIC_REPLACE",
StaticReplacement = "user@redacted.invalid"
}
{ "strategy": "STATIC_REPLACE", "staticReplacement": "user@redacted.invalid" }
CRYPTO_REPLACE
Encrypts the token with AES-GCM and replaces it with the ciphertext wrapped in double braces, e.g. {{<base64>}} (the Base64 payload is nonce || ciphertext || tag). Requires a Crypto block on the Policy whose key is a hex-encoded 16, 24, or 32-byte AES key. Falls back to REDACT if the policy has no Crypto configuration.
var policy = new Policy
{
Name = "encrypted",
Crypto = new Crypto
{
// Hex-encoded AES key (32 hex chars = 16 bytes, 48 = 24, 64 = 32).
Key = Convert.ToHexString(aesKey)
},
Identifiers = new Identifiers
{
Ssn = new Ssn
{
Strategies = new List<SsnFilterStrategy>
{
new SsnFilterStrategy { Strategy = "CRYPTO_REPLACE" }
}
}
}
};
The key may also be supplied as an
env:NAMEreference, which is resolved from the environment variableNAMEat filter time.
FPE_ENCRYPT_REPLACE
Format-preserving encryption using the FF3-1 cipher. Requires an Fpe block on the Policy with a hex-encoded key and a tweak. The tweak is required (FF3-1 needs a 56- or 64-bit tweak); the strategy falls back to REDACT when the Fpe block, key, or tweak is missing.
var policy = new Policy
{
Name = "fpe-policy",
Fpe = new Fpe
{
Key = "EF4359D8D580AA4F7F036D6F04FC6A94", // hex-encoded key
Tweak = "D8E7920AFA330A73" // hex-encoded tweak (required)
},
Identifiers = new Identifiers
{
CreditCard = new CreditCard
{
Strategies = new List<CreditCardFilterStrategy>
{
new CreditCardFilterStrategy { Strategy = "FPE_ENCRYPT_REPLACE" }
}
}
}
};
Like the AES key, the FPE key and tweak may be supplied as
env:NAMEreferences.
HASH_SHA256_REPLACE
Replaces the token with its lower-case SHA-256 hex digest. Optionally appends a random salt before hashing when salt: true is set.
new EmailAddressFilterStrategy
{
Strategy = "HASH_SHA256_REPLACE",
Salt = true // prepend random salt before hashing
}
{ "strategy": "HASH_SHA256_REPLACE", "salt": true }
LAST_4
Keeps the last four characters of the token and discards the rest. If the token is shorter than four characters, the full token is returned.
new CreditCardFilterStrategy { Strategy = "LAST_4" }
Output example: 1234 (from 4111-1111-1111-1234).
MASK
Replaces characters with a mask character (default *). Use maskLength to control how many characters are written:
maskLength value |
Behaviour |
|---|---|
"same" (default) |
Mask has the same length as the original token |
Integer string, e.g. "6" |
Mask has exactly that many characters (capped at token length) |
new SsnFilterStrategy
{
Strategy = "MASK",
MaskCharacter = "#",
MaskLength = "same"
}
{ "strategy": "MASK", "maskCharacter": "#", "maskLength": "6" }
SAME
Marks the token as detected but leaves the text unchanged. Useful when you want spans and metadata without altering the output.
new PhoneNumberFilterStrategy { Strategy = "SAME" }
TRUNCATE
Keeps only the first character of the token.
new EmailAddressFilterStrategy { Strategy = "TRUNCATE" }
SHIFT_DATE
Date filters only. Applies to
DateFilterStrategy; ignored by all other filter types.
Shifts a detected date forward or backward by a configurable number of days, months, and/or years while preserving the original date format. All three offsets default to 0 and can be combined freely. Negative values shift the date into the past.
Supported date formats
| Example | Format |
|---|---|
1/15/1990 |
Numeric M/D/YYYY |
January 15, 1990 |
Full month name |
15-Jan-1990 |
Day-abbreviated-month-year |
Jan 15, 1990 |
Abbreviated month name |
If the detected token cannot be parsed as a date, or if the Date filter type is not active, SHIFT_DATE falls back to REDACT.
Properties
| Property | JSON key | Type | Default | Description |
|---|---|---|---|---|
Days |
days |
int |
0 |
Days to add (negative to subtract) |
Months |
months |
int |
0 |
Months to add (negative to subtract) |
Years |
years |
int |
0 |
Years to add (negative to subtract) |
C# example
using Phileas.Policy.Filters;
using Phileas.Policy.Filters.Strategies;
var policy = new Policy
{
Name = "date-shift-policy",
Identifiers = new Identifiers
{
Date = new Date
{
Strategies = new List<DateFilterStrategy>
{
new DateFilterStrategy
{
Strategy = "SHIFT_DATE",
Years = -1,
Days = 14
}
}
}
}
};
JSON policy example
{
"name": "date-shift-policy",
"identifiers": {
"date": {
"dateFilterStrategies": [{
"strategy": "SHIFT_DATE",
"years": -1,
"days": 14
}]
}
}
}
Example transformation
| Input | Output |
|---|---|
DOB: January 15, 1990 |
DOB: January 29, 1989 |
Admitted: 3/1/2024 |
Admitted: 3/15/2023 |
Salting
Any strategy can optionally append a random 16-byte Base64 salt to the token before processing by setting salt: true. The generated salt is included in the Span.Salt field of the result so it can be recorded for auditing or reproduction.
new SsnFilterStrategy
{
Strategy = "HASH_SHA256_REPLACE",
Salt = true
}
Strategy Conditions
Strategies can include a condition property that controls when they are applied. When multiple strategies are defined, phileas-dotnet evaluates their conditions in order and applies the first strategy whose condition evaluates to true.
new EmailAddressFilterStrategy
{
Strategy = "MASK",
Condition = "confidence > 0.8 and context == \"internal\""
}
Supported condition fields:
- confidence - Detection confidence (0.0 to 1.0)
- context - Context name passed to FilterService.Filter()
- token - The detected text value
- type - Classification type (e.g., "PER", "LOC")
- population - Census population of a detected ZIP code
Supported operators:
- Comparison: ==, !=, >, <, >=, <=, is, is not
- String: startswith
- Logical: and
See Filter Conditions for detailed examples and usage patterns.
Configuring Strategies Per Identifier
Each identifier type has a corresponding strategy class (e.g. SsnFilterStrategy, EmailAddressFilterStrategy). Set the Strategies list on the identifier:
var policy = new Policy
{
Name = "multi-strategy",
Identifiers = new Identifiers
{
Ssn = new Ssn
{
Strategies = new List<SsnFilterStrategy>
{
new SsnFilterStrategy { Strategy = "MASK" }
}
},
EmailAddress = new EmailAddress
{
Strategies = new List<EmailAddressFilterStrategy>
{
new EmailAddressFilterStrategy { Strategy = "HASH_SHA256_REPLACE" }
}
},
PhoneNumber = new PhoneNumber
{
Strategies = new List<PhoneNumberFilterStrategy>
{
new PhoneNumberFilterStrategy { Strategy = "LAST_4" }
}
}
}
};