Skip to main content

Step 4: Hash Identifiers

Pseudonymize identifiers for cohort analytics. Hashing does not establish anonymity or prevent re-identification.

The Goal​

  • Customer ID → Salted hash (count unique customers without knowing who)
  • Email → Retain the domain in this authored example
  • IBAN → Retain the country prefix in this authored example
  • IP Address → /16 subnet (regional analysis)

Why Hash vs. Delete?​

Hashed identifiers enable valuable analytics:

  • Count unique customers per merchant
  • Track customer cohort behavior over time
  • Detect patterns across pseudonymous IDs

A secret salt makes candidate matching harder; a known salt does not protect predictable identifiers.

Implementation​

See the hashing processor in the canonical reference and its captured input and output in the Explorer. The fixed fallback salt is for the example; require a managed secret when adapting it.

Understanding the Code​

ExpressionWhat It Does
(value + $salt).hash("sha256")Salted SHA-256 digest bytes
.encode("hex")Encode digest bytes as hexadecimal text
.slice(0, 12)Truncate hexadecimal text; collisions remain possible
.split("@").index(1)Get domain from email
.slice(0, 2)Get first 2 characters (country code from IBAN)

Why Salting Matters​

Without salt: Attacker with customer ID list can hash each and match.

With salt: Attacker needs both the ID list AND your secret salt.

customer_id = "CUST-DE-12345"

# Without salt (vulnerable):
hash("CUST-DE-12345") → "a1b2c3d4..." # Rainbow table attack possible

# With the demonstration salt:
hash("CUST-DE-12345" + "secret-salt-xyz") → "x9y8z7w6..." # Requires salt knowledge

Expected Output​

Use the Explorer checkpoints for the sample input and resulting fields.

Analytics Value Preserved​

OriginalPseudonymous outputAnalytics Possible
customer_idanonymized_customer_idCount unique customers, cohort tracking
customer_emailemail_domainB2B vs B2C ratio, company analysis
ibanbank_countryGeographic distribution by bank country
ip_addressip_subnetRegional traffic patterns

Adaptation checks​

Consistent Hashing Across Pipelines​

Document your hash specification:

Reference notes (not pipeline YAML)
# HASH SPECIFICATION (document for all pipelines):
# Algorithm: SHA-256
# Salt: env("ANONYMIZATION_SALT")
# Encoding: hexadecimal before truncation
# Truncation: 12 characters
# Input format: Raw value as string

Handle IPv6​

Support both IPv4 and IPv6:

root.ip_subnet = if this.ip_address.contains(":") {
# IPv6: keep first 4 groups
this.ip_address.split(":").slice(0, 4).join(":") + "::/64"
} else if this.ip_address.contains(".") {
# IPv4: keep first 2 octets
this.ip_address.split(".").slice(0, 2).join(".") + ".0.0/16"
} else {
"unknown"
}

Email Domain Categorization​

Enhance domain extraction with categorization:

let domain = this.customer_email.split("@").index(1).lowercase()
let freemail = ["gmail.com", "yahoo.com", "hotmail.com", "outlook.com"]

root.email_domain = domain
root.email_type = if $freemail.contains(domain) { "consumer" } else { "business" }

Next Step​