How to Hash Email Addresses in a PII Masking Pipeline in Python
Replaces every email address in a text string with its SHA-256 hash to protect personally identifiable information (PII).
Python code
17 linesimport hashlib
import re
def hash_email(email: str) -> str:
"""Mask an email address by hashing it with SHA-256."""
normalized = email.strip().lower()
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
def mask_pii_emails(text: str) -> str:
"""Replace all email addresses in text with their hashed versions."""
email_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
return re.sub(email_pattern, lambda m: hash_email(m.group(0)), text)
if __name__ == "__main__":
sample_text = "Contact: Alice.Smith@Example.com or bob_jones@test.org for info."
masked_text = mask_pii_emails(sample_text)
print(masked_text)
Output
Contact: 9f9c33f7d3e4c8b1a8a9d5f7e4c2a1b0c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8 or c0a8d5f6e7b9c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 for info.
How it works
The re.sub call uses a regex pattern to find every email address in the input text. For each match, the lambda function calls hash_email, which normalizes the email by stripping whitespace and converting to lowercase before applying SHA-256. The hash is a fixed-length hexadecimal string, so masking preserves referential integrity for joins or analytics without exposing the original value. Using the standard library hashlib keeps the pipeline dependency-free and fast.
Common mistakes
- Hashing on a case-sensitive basis, breaking joins if the same email appears with different casing
- Using a random salt each time, which makes the hash non-deterministic and useless for matching
- Forgetting to strip whitespace, producing different hashes for 'email@x.com ' vs 'email@x.com'
- Assuming SHA-256 alone is sufficient; for highly sensitive data, add a pepper or use HMAC
Variations
- Use `hmac.new` with a secret key to prevent rainbow table attacks
- Replace emails with `shorter` hashes (e.g., first 12 chars) to reduce storage while keeping some uniqueness
Real-world use cases
- Anonymizing customer email addresses in logs before writing to centralized monitoring for privacy compliance.
- Creating a consistent join key for analytics without exposing raw emails across different data warehouse tables.
- Masking PII in text fields of support ticket exports before sharing with third-party vendors for processing.
Sponsored
More from Data pipelines & processing
- Add a UUID Surrogate Key to Each Row in a CSV with Python easy
- Attach Source File Metadata to Records in Python easy
- Build a Python Utility That Detects Duplicate Records Across Multiple Excel Sheets medium
- Check Null Rate Threshold in PySpark DataFrame medium
- Count Records Processed per Category in Python easy
- Create Data Helper Functions in Python for Beginners easy
Keep learning
Related tutorials and quizzes for this topic.