Enterprise PII Masking, Tokenization & Encryption Module
Project description
PII Engine - Production Ready Pseudonymization & Tokenization
Enterprise-grade PII (Personally Identifiable Information) processing engine with field-level tokenization, deterministic pseudonymization, and AES-256 encryption.
๐ Quick Start Guide
Step 1: Environment Setup
1.1 Copy Environment File
cp .env.example .env
1.2 Update .env with Your Database Credentials
# Generate encryption key first
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# Edit .env file
PII_ENC_KEY=your-generated-key-here
# Your Database Configuration
DB_HOST=your-database-host
DB_PORT=3306
DB_USER=your-username
DB_PASSWORD=your-password
DB_NAME=your-database-name
Step 2: Configure Your Tables
2.1 Edit pii_engine/config/fabricator.json
{
"product_specific_config": {
"your_product_name": {
"fabricator_name": "YOUR_FABRICATOR",
"token_size": "8",
"prefix": "YOUR"
}
},
"schema": [
{
"table": "users",
"columns": [
{"name": "email", "type": "varchar(255)", "mask": true, "pseudo": true},
{"name": "mobile_number", "type": "varchar(20)", "mask": true, "pseudo": true},
{"name": "password", "type": "varchar(255)", "mask": true, "pseudo": true},
{"name": "username", "type": "varchar(255)", "mask": false, "pseudo": false}
]
}
]
}
Field Configuration:
"mask": true, "pseudo": true= Field will be pseudonymized with individual token"mask": false, "pseudo": false= Field remains unchanged
Step 3: Database Setup
3.1 Add JSON Column to Tables
python add_pii_json_column.py
This adds pii_data JSON column to store field-level tokens and encrypted originals.
Step 4: Test Your Setup
4.1 Test Processing
python main.py
This will:
- Process sample records with field-level tokenization
- Generate unique tokens per field (YOUR_XXXXXXXX format)
- Store encrypted originals in same table JSON column
- Update main table with pseudonymized data
- Performance: ~0.9 seconds per field
4.2 View Results
python view_tokens.py
๐ Production Usage
Field-Level Processing
from pii_engine.core.clean_processor import PIIProcessor
processor = PIIProcessor()
# Process individual field
pseudo_value, token = processor.process_field(
"users", "email", "john@example.com", user_id
)
# Retrieve original data
original_email = processor.vault.retrieve_field_from_main_table(
"users", user_id, "email"
)
Important: Duplicate Protection The masking service automatically detects and skips already processed fields, preventing data corruption during:
- Multiple INSERT attempts on same record
- Repeated UPDATE operations on same field
- Mixed INSERT/UPDATE scenarios
No additional code needed - protection is built-in.
Bulk Processing
python bulk_processor.py
- Processes thousands of existing records
- Batch processing with progress tracking
- Converts existing data to masked format
๐๏ธ How It Works
Field-Level Data Flow
Original Data โ Individual Tokens โ Pseudonymization โ Same Table Storage
โ โ โ โ
"john@email.com" โ YOUR_a1b2c3d4 โ "fake@example.com" โ Main Table
"555-1234" โ YOUR_e5f6g7h8 โ "555-9876" โ Main Table
Original Data โ AES-256 Encryption โ JSON Storage
โ โ โ
"john@email.com" โ Encrypted Value โ pii_data JSON column
Token Format
- Prefix: Configured in fabricator.json (e.g., "YOUR")
- Format: YOUR_XXXXXXXX (8 character hash)
- Unique: Each field gets individual token
- Deterministic: Same field value = same pseudonym
Storage Architecture
Main Table (users):
- Contains pseudonymized data in original columns
pii_dataJSON column stores tokens and encrypted originals- No separate vault table needed
JSON Structure Example:
{
"email": {
"token": "YOUR_a1b2c3d4",
"encrypted_value": "encrypted_original_email"
},
"mobile_number": {
"token": "YOUR_e5f6g7h8",
"encrypted_value": "encrypted_original_mobile"
}
}
๐ง Production Integration Guide
Phase 1: Service Integration
Step 1: Import Production-Ready Functions
# Import our production-ready wrapper functions
from pii_engine.wrappers import process_pii_fields, retrieve_original_data
Step 2: Replace Your Existing Functions
Delete your hardcoded process_pii_fields() and retrieve_original_data() functions and use ours.
Phase 2: Database Operations
A. INSERT Operations (User Registration)
Current Pattern:
cursor.execute("INSERT INTO jobseekers (email, mobile_number, ...) VALUES (%s, %s, ...)",
(email, mobile, ...))
Modified Pattern:
# Process PII fields before insert
user_data = {'email': email, 'mobile_number': mobile_number, 'first_name': first_name}
processed_data = process_pii_fields("jobseekers", "pseudo", None, user_data)
# Extract fields and tokenSet
actor_details = processed_data['fields']
actor_details["token_set"] = processed_data['TokenSet']
# Insert with pseudonymized data
cursor.execute("INSERT INTO jobseekers (email, mobile_number, first_name, token_set, ...) VALUES (%s, %s, %s, %s, ...)",
(actor_details['email'], actor_details['mobile_number'], actor_details['first_name'],
json.dumps(actor_details['token_set']), ...))
B. UPDATE Operations
Current Pattern:
cursor.execute("UPDATE jobseekers SET email = %s WHERE id = %s", (new_email, user_id))
Modified Pattern:
# Process new PII value
update_data = {'email': new_email}
processed_data = process_pii_fields("jobseekers", "pseudo", user_id, update_data)
cursor.execute("UPDATE jobseekers SET email = %s WHERE id = %s",
(processed_data['fields']['email'], user_id))
C. SELECT Operations (Data Retrieval)
Current Pattern:
cursor.execute("SELECT email, mobile_number FROM jobseekers WHERE id = %s", (user_id,))
result = cursor.fetchone()
Modified Pattern:
cursor.execute("SELECT email, mobile_number, token_set FROM jobseekers WHERE id = %s", (user_id,))
result = cursor.fetchone()
# For admin access - retrieve original data
if user_role == "admin":
original_email = retrieve_original_data("jobseekers", user_id, "email")
result['email'] = original_email
D. Admin Data Retrieval Endpoint
@router.get("/admin/user/{user_id}/original")
async def get_original_user_data(user_id: int, user_role: str = Depends(get_user_role)):
if user_role != "admin":
raise HTTPException(status_code=403, detail="Admin access required")
# Retrieve original PII data
original_email = retrieve_original_data("jobseekers", user_id, "email")
original_mobile = retrieve_original_data("jobseekers", user_id, "mobile_number")
return {
"user_id": user_id,
"original_email": original_email,
"original_mobile": original_mobile
}
Phase 3: Function Signatures
process_pii_fields()
process_pii_fields(table_name: str, process: str, record_id: Optional[int], field_data: dict) -> dict
table_name: "jobseekers" or "employers"process: "pseudo" or "mask"record_id: User ID (None during signup)field_data: Input JSON data- Returns:
{'TokenSet': token_data, 'fields': processed_data}
retrieve_original_data()
retrieve_original_data(table_name: str, record_id: int, field_name: str) -> str
- Returns: Original field value for admin access
Phase 4: Database Setup
Add token_set column to your tables:
ALTER TABLE jobseekers ADD COLUMN token_set JSON;
ALTER TABLE employers ADD COLUMN token_set JSON;
๐ Integration Steps Summary
For Your Team:
-
Import Functions (1 line):
from pii_engine.wrappers import process_pii_fields, retrieve_original_data
-
Delete Your Functions (~20 lines):
- Remove your hardcoded
process_pii_fields()function - Remove your hardcoded
retrieve_original_data()function
- Remove your hardcoded
-
Add Database Column:
ALTER TABLE jobseekers ADD COLUMN token_set JSON; ALTER TABLE employers ADD COLUMN token_set JSON;
-
That's It! Your existing code works as-is.
Console Output:
>>> PROCESSING TABLE: jobseekers (process: pseudo)
๐ What Happens During Processing
Why record_id is None during signup:
- Signup Flow: User doesn't exist yet โ
record_id = None - Update Flow: User exists โ
record_id = actual_user_id - Our Service: Handles both cases automatically
Input Data:
{
"first_name": "John",
"email": "john@example.com",
"uid": 633
}
Console Output:
>>> PROCESSING TABLE: jobseekers (process: pseudo)
Returned Data:
{
"TokenSet": {
"first_name": "EE_4bd1511e",
"last_name": "EE_b83eef5f",
"email": "EE_695caa3c",
"mobile_code": "EE_4151c52c",
"mobile_number": "EE_c12e9f85",
"gender": "EE_b1a66072",
"date_of_birth": "EE_123364e0",
"country": "EE_b8123438",
"state": "EE_8349e493",
"city": "EE_cf3f05e7",
"zip_code": "EE_2e7e659a",
"address_1": "EE_c9aafc13",
"address_2": "EE_29b0cb77",
"cv_file_path": "EE_2bc76800",
"agreed_to_terms": "Y",
"usertype": "JS",
"usersubtype": "JS",
"status": "A",
"uid": 633,
"username": "578cb6087149"
},
"fields": {
"first_name": "***",
"last_name": "***",
"email": "user123@fake.edu",
"mobile_code": "***",
"mobile_number": "***",
"gender": "***",
"date_of_birth": "1990-01-01",
"country": "***",
"state": "***",
"city": "***",
"zip_code": "***",
"address_1": "***",
"address_2": "***",
"cv_file_path": "***",
"agreed_to_terms": "Y",
"usertype": "JS",
"usersubtype": "JS",
"status": "A",
"uid": 633,
"username": "578cb6087149"
}
}
Database Result:
- Main table gets pseudonymized data
token_setcolumn stores complete audit trail- Admin can retrieve original data anytime
๐ก๏ธ Security Features
- Field-Level Tokenization: Individual tokens per field per record
- Same Table Storage: No separate vault table needed
- AES-256 Encryption: Military-grade encryption for original data
- Deterministic Pseudonymization: Same input = same fake output
- Complete Reversibility: Admin can retrieve original data using tokens
- Configurable Processing: Teams control which fields to mask via fabricator.json
๐ Performance Metrics
Current Performance:
- Field Processing: ~0.9 seconds per field
- Record Processing: ~5-6 seconds per record (multiple fields)
- Bulk Processing: 1000+ records with progress tracking
- Database: Optimized connection reuse
Test Results:
Record 31: dchakras@gmail.com โ sergiorivera@example.net (Token: EE_7b659ff3)
Record 32: dchakras1@gmail.com โ michaelcarter@example.net (Token: EE_dc06a015)
Record 33: sonjoy.c@tnsservices.com โ cynthia30@example.com (Token: EE_bac2065b)
Record 34: naveenkumar.k@tnsservices.com โ russell53@example.com (Token: EE_f24e8e6a)
๐ File Structure
masking-service/
โโโ pii_engine/ # Core PII processing package
โ โโโ config/
โ โ โโโ fabricator.json # Table configuration
โ โ โโโ config.py # Config loader
โ โโโ core/
โ โ โโโ clean_processor.py # Main processor
โ โ โโโ pseudonymizer.py # Data pseudonymization
โ โ โโโ token_generator.py # Token generation
โ โโโ utils/
โ โ โโโ vault.py # JSON storage & encryption
โ โโโ wrappers/
โ โโโ __init__.py # Wrapper package
โ โโโ integration_wrappers.py # Production wrapper functions
โโโ main.py # Test processing (field-level)
โโโ bulk_processor.py # Bulk processing (production)
โโโ add_pii_json_column.py # Database setup
โโโ demo_reset.py # Reset test data
โโโ view_tokens.py # Token inspection
โโโ .env # Database credentials
โโโ README.md # This guide
๐ Troubleshooting
Common Issues
1. Database Connection Error
- Check
.envcredentials - Verify database server is accessible
2. No Maskable Fields Found
- Update
fabricator.jsonwith your table schema - Ensure
"mask": true, "pseudo": truefor PII fields
3. JSON Column Missing
- Run
python add_pii_json_column.pyto addpii_datacolumn - Check column exists:
SHOW COLUMNS FROM your_table LIKE 'pii_data';
4. Performance Issues
- First field takes ~3s (connection setup), subsequent fields ~0.9s
- Use connection reuse for better performance
Commands Reference
# Database setup
python add_pii_json_column.py
# Test processing
python main.py
# View results
python view_tokens.py
# Bulk processing
python bulk_processor.py
# Reset test data
python demo_reset.py
๐ Key Features
- โ Field-Level Tokenization - Individual tokens per field
- โ Same Table Storage - No separate vault table needed
- โ Configuration-Driven - Teams control masking via fabricator.json
- โ Fast Performance - ~0.9s per field processing
- โ AES-256 Encryption - Military-grade security
- โ Complete Reversibility - Admin data retrieval
- โ Production Ready - Bulk processing support
Ready to Integrate ๐ฏ
- Update
.envโ 2. Configurefabricator.jsonโ 3. Runpython add_pii_json_column.pyโ 4. Test withpython main.pyโ Success!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tns_pii_engine-1.0.0.tar.gz.
File metadata
- Download URL: tns_pii_engine-1.0.0.tar.gz
- Upload date:
- Size: 35.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30456447a483a3fd742402a4beec4ec93a352a7e218932ec0a0c28f4ecfe5628
|
|
| MD5 |
847dfc76e5774f3de83066320e9dc906
|
|
| BLAKE2b-256 |
f72c097c688223e5c80ad3cc7238ed4bcac3314c89aff73ce7481f9ff2889cf7
|
File details
Details for the file tns_pii_engine-1.0.0-py3-none-any.whl.
File metadata
- Download URL: tns_pii_engine-1.0.0-py3-none-any.whl
- Upload date:
- Size: 35.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc3ea4057a576439ae6cb4c9252a044c66618ae9b18f55547b652de881a7a1a2
|
|
| MD5 |
beb0059c183a71a548423621470e1b34
|
|
| BLAKE2b-256 |
600bcb37b486966a7c7a5215c486c55a34cd96a9e6bcd875ab458f0f8131b74e
|