Data Masking and Anonymization of Sensitive Data
Data masking and anonymization represent essential privacy protection techniques that transform sensitive data into non-identifiable or obfuscated versions, allowing organizations to use information in development, testing, analytics, and machine learning environments without exposing the personal or confidential data of data subjects. In an increasingly stringent regulatory context, with legislation such as LGPD in Brazil and GDPR in Europe imposing severe fines for the inadequate exposure of Personally Identifiable Information (PII), and considering that developers frequently need to work with copies of production data containing millions of customer records for debugging, performance testing, and feature development, the absence of adequate protections creates monumental risks of data leakage, regulatory non-compliance, and irreparable damage to corporate reputation. The technical challenge lies in applying transformations that preserve the usefulness of the data for legitimate purposes - maintaining statistical distributions, relationships between tables, and format validations - while eliminating the possibility of re-identifying individuals. This article explores static and dynamic masking techniques, irreversible anonymization methods, reversible pseudonymization with access control, tokenization for reference preservation, differential privacy for protection in aggregations, and practical implementations in SQL databases, NoSQL, and modern data warehouses.
Data Masking vs Anonymization vs Pseudonymization
Data Masking (Concealment)
- Replaces data with fictitious but realistic values
- Preserves data format and type
- Irreversible (without a reversal key)
- Use: Non-production environments
Anonymization (Irreversible)
- Completely removes the possibility of identification
- Does not allow reconstruction of the original data
- Anonymized data is no longer PII (GDPR/LGPD)
- Use: Public analytics, research datasets
Pseudonymization (Reversible)
- Replaces direct identifiers with pseudonyms
- Reversible with a mapping key/token
- Still considered PII (requires LGPD/GDPR protection)
- Use: Production with controlled access
Data Masking Techniques
1. Substitution
-- Replaces real data with fictitious but realistic values
Original: João Silva, [email protected], 123.456.789-00
Masked: Maria Santos, [email protected], 987.654.321-00
-- SQL example
UPDATE users_dev
SET
email = CONCAT('user', id, '@example.com'),
cpf = fake_cpf_generator(),
name = fake_name_generator();
2. Shuffling
-- Redistributes existing values across records
-- Preserves data distribution but breaks correlation
User 1: João, [email protected]
User 2: Maria, [email protected]
After shuffling:
User 1: João, [email protected] -- Email of user 2
User 2: Maria, [email protected] -- Email of user 1
-- Useful for tests, keeping real values but not linkable
3. Character Masking
-- Hides part of the data, keeps partial visibility
Original: 1234-5678-9012-3456
Masked: ****-****-****-3456
Original: [email protected]
Masked: j***@email.com
-- SQL
SELECT
CONCAT(
REPEAT('*', LENGTH(name) - 2),
SUBSTRING(name, -2)
) as masked_name,
CONCAT(
SUBSTRING(email, 1, 1),
'***@',
SUBSTRING_INDEX(email, '@', -1)
) as masked_email;
4. Nulling Out
-- Replaces with NULL or a default value
-- Use: Extremely sensitive data with no usefulness in tests
UPDATE users_dev
SET
social_security_number = NULL,
credit_card = NULL,
password_hash = 'REDACTED';
5. Deterministic Hashing
-- Consistent hash maintains relationships
-- The same input always generates the same output
-- Useful for JOINs and FKs
-- PostgreSQL
UPDATE users_dev
SET email = encode(
digest(email || 'salt-secret', 'sha256'),
'hex'
) || '@masked.com';
-- The same email always becomes the same hash
-- Preserves referential integrity
Tokenization
-- Token system with a separate vault
-- Token mapping stored in a secure location
-- Original data never leaves the vault
1. Client sends: CPF 123.456.789-00
2. Tokenization service returns: TOK_8f3d92a1b4c5
3. Application stores only the token
4. To detokenize: Token → Vault → Real value
-- Advantages:
- Reversible with authorization
- Real data isolated in a secure vault
- PCI-DSS compliance (credit cards)
-- Node.js example
const token = await tokenizationService.tokenize({
type: 'cpf',
value: '123.456.789-00',
context: 'user-registration'
});
// Store: token = "TOK_8f3d92a1b4c5"
// Detokenize (requires permission)
const realValue = await tokenizationService.detokenize(token);
Pseudonymization
-- Replaces direct identifiers with pseudonyms
-- Retains additional data for usefulness
-- Reversible via a controlled lookup table
Original:
User ID: 12345
Name: João Silva
Email: [email protected]
Age: 35
Pseudonymized:
Pseudonym: PSEUDO_9f8e7d6c
Name: [REMOVED]
Email: [REMOVED]
Age: 35 -- Retained for analytics
Cohort: 30-40 -- Generalized
-- Lookup table (restricted access)
PSEUDO_9f8e7d6c → User 12345
Irreversible Anonymization
K-Anonymity
-- Ensures each record is indistinguishable from at least k-1 others
-- Generalization and suppression of quasi-identifiers
Original:
| ZIP Code | Age | Gender | Disease |
|----------|-----|--------|---------|
| 12345 | 28 | M | HIV |
| 12346 | 29 | M | HIV |
| 54321 | 35 | F | Cancer |
2-anonymous (k=2):
| ZIP Code | Age | Gender | Disease |
|----------|-------|--------|---------|
| 1234* | 25-30 | M | HIV |
| 1234* | 25-30 | M | HIV |
| 5432* | 30-40 | F | Cancer |
Differential Privacy
-- Adds controlled noise to aggregated queries
-- Impossible to determine whether an individual is in the dataset
-- Example: Query "how many users have HIV?"
Real answer: 150
DP answer: 150 + Laplace_noise(ε) = 152
-- ε (epsilon): Privacy budget
-- small ε = more privacy, less accuracy
-- large ε = less privacy, more accuracy
-- Python implementation
import numpy as np
def laplace_mechanism(true_answer, sensitivity, epsilon):
noise = np.random.laplace(0, sensitivity/epsilon)
return true_answer + noise
# Query: COUNT(users WHERE condition)
true_count = 150
dp_count = laplace_mechanism(true_count, sensitivity=1, epsilon=0.1)
Practical Implementation
PostgreSQL Data Masking
-- Extension: anon (PostgreSQL Anonymizer)
CREATE EXTENSION anon CASCADE;
SELECT anon.init();
-- Declare masking rules
SECURITY LABEL FOR anon ON COLUMN users.email
IS 'MASKED WITH FUNCTION anon.fake_email()';
SECURITY LABEL FOR anon ON COLUMN users.name
IS 'MASKED WITH FUNCTION anon.fake_first_name() || '' '' || anon.fake_last_name()';
-- Create masked role
CREATE ROLE masked_user;
SELECT anon.start_dynamic_masking();
GRANT SELECT ON users TO masked_user;
-- When masked_user queries, they see masked data
-- Privileged roles see real data
Application-Level Masking (Node.js)
const { faker } = require('@faker-js/faker');
const crypto = require('crypto');
class DataMasker {
maskEmail(email) {
const [local, domain] = email.split('@');
return \`\${local[0]}***@\$\`;
}
maskCPF(cpf) {
return cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '***.$2.$3-**');
}
generateFakeEmail() {
return faker.internet.email();
}
generateFakeName() {
return faker.person.fullName();
}
deterministicHash(value, salt) {
return crypto
.createHmac('sha256', salt)
.update(value)
.digest('hex');
}
}
// Usage
const masker = new DataMasker();
const user = {
email: '[email protected]',
cpf: '12345678900'
};
const masked = {
email: masker.maskEmail(user.email), // j***@email.com
cpf: masker.maskCPF(user.cpf) // ***.456.789-**
};
Tools and Solutions
- PostgreSQL Anonymizer: Open-source extension for PostgreSQL
- Oracle Data Masking: Built-in enterprise solution
- Microsoft SQL Server: Dynamic Data Masking feature
- Delphix: Enterprise data masking platform
- Informatica: Persistent Data Masking
- Faker.js: Library for generating fictitious data
- ARX Data Anonymization Tool: Open-source k-anonymity
- Google Differential Privacy: DP library
Best Practices
- Multi-layer masking: Database + application + visualization
- Preserve usefulness: Maintain formats, statistical distributions
- Referential consistency: Use deterministic hashing for FKs
- Automated pipelines: Automatic masking on dev/staging refresh
- Re-identification testing: Validate that anonymization is irreversible
- PII documentation: Catalog all sensitive fields
- Access controls: Who can see real vs masked data
- Audit logging: Track access to unmasked data
Final Recommendations
Implement static masking in development environments with automatic refresh from production. Use pseudonymization in production for data that must be auditable. Apply tokenization for payments (PCI-DSS). For public datasets, ensure a minimum k-anonymity of 5 and consider differential privacy. Always test with re-identification attempts before publishing anonymized data.
