数据匿名化
为测试、开发和分析安全使用而匿名化生产数据。自动检测个人身份信息(PII),应用适当的匿名化策略(掩码、哈希、合成替换、概括),并生成保留数据关系和统计属性的真实假数据。使用时: "匿名化数据"、"掩码 PII"、"从生产数据创建测试数据"、"GDPR 合规"、"数据掩码"、"删除个人数据"、"清理数据库"、"生成假数据",或在为非生产使用准备生产数据时。
命令
步骤 1:扫描 PII 模式
# 扫描文件以查找常见的 PII 模式
rg -n "(\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b)" --type-not binary 2>/dev/null | head -20
echo "--- 上面找到电子邮件 ---"
rg -n "\\b\\d{3}[-.]?\\d{2}[-.]?\\d{4}\\b" --type-not binary 2>/dev/null | head -20
echo "--- 上面找到类似 SSN 的模式 ---"
rg -n "\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b" --type-not binary 2>/dev/null | head -20
echo "--- 上面找到电话号码 ---"
rg -n "\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b" --type-not binary 2>/dev/null | head -20
echo "--- 上面找到类似信用卡的模式 ---"
步骤 2:扫描数据库模式
# 找到可能包含 PII 的列(按名称模式)
python3 -c " pii_column_patterns = [ 'email', 'phone', 'address', 'street', 'city', 'zip', 'postal', 'ssn', 'social_security', 'tax_id', 'national_id', 'first_name', 'last_name', 'full_name', 'name', 'birth', 'dob', 'date_of_birth', 'age', 'credit_card', 'card_number', 'cvv', 'expiry', 'ip_address', 'ip', 'user_agent', 'password', 'secret', 'token', 'api_key', 'latitude', 'longitude', 'lat', 'lng', 'geo', 'photo', 'avatar', 'image_url', 'salary', 'income', 'bank_account', 'iban', 'routing', ] # 解析 SQL 转储或迁移文件中的模式
import sys
for pattern in pii_column_patterns:
print(f' - {pattern}
')
print('\\n使用这些模式来 grep您的数据库模式:')
print('rg -i \"(\" + \"|\".join(pii_column_patterns[:5]) + \")\" migrations/ schema.sql') "
步骤 3:分类敏感性级别数据类型策略
关键 SSN、信用卡、密码、API 密钥 删除或哈希(不可逆)
高 电子邮件、电话、全名、地址 合成替换
中 日期、IP 地址、位置 概括(仅年、/24 子网)
低 年龄范围、城市、工作职务 保留或稍微扰乱
策略 1:合成替换(推荐用于测试数据)
# 生成保留格式和关系的真实假数据
import hashlib
def anonymize_email(email):
"""一致的假电子邮件 ——相同输入始终产生相同输出"""
h = hashlib.sha256(email.encode()).hexdigest()[:8]
domain = email.split('@')[1] if '@' in email else 'example.com'
return f"user_{h}@test-{domain}"
def anonymize_name(name):
"""替换为一致的假名称"""
from faker import Faker
fake = Faker()
fake.seed_instance(hash(name) % (232))
return fake.name()
def anonymize_phone(phone):
"""保留格式,替换数字"""
import re
h = hashlib.sha256(phone.encode()).hexdigest()
digits = [c for c in h if c.isdigit()]
result = ''
d = 0
for c in phone:
if c.isdigit():
result += digits[d % len(digits)]
d += 1
else:
result += c
return result
def anonymize_address(address):
"""替换为同一区域的假地址"""
from faker import Faker
fake = Faker()
fake.seed_instance(hash(address) % (232))
return fake.address()
策略 2:掩码(快速,用于日志/导出)
def mask_email(email):
parts = email.split('@')
return f"{parts[0][:2]}@{parts[1]}" if '@' in email else ''
def mask_phone(phone):
return phone[:3] + '' + phone[-2:]
def mask_ssn(ssn):
return '--' + ssn[-4:]
def mask_card(card):
return '---' + card[-4:]
策略 3:SQL 级别匿名化 —— PostgreSQL 匿名化脚本
UPDATE users SET email = 'user_' || md5(email) || '@example.com',
first_name = 'User',
last_name = 'Test_' || substring(md5(last_name) from 1 for 6),
phone = '+1' || lpad(abs(hashtext(phone))::text, 10, '0'),
address_line1 = floor(random() 9999)::text || ' Test Street',
city = 'Testville',
zip_code = lpad(abs(hashtext(zip_code))::text, 5, '0'),
date_of_birth = date_of_birth - (random()
365)::int interval '1 day',
ssn = NULL
WHERE true;
-- 验证没有真实数据剩余
SELECT email FROM users WHERE email NOT LIKE '%@example.com' LIMIT 5;
匿名化后,验证:
没有真实电子邮件地址剩余(检查已知模式)
没有真实电话号码(验证格式但不是真实号码)
统计属性保留(年龄分布、地理分布)
引用完整性保持(FK 关系完整)
唯一性约束尊重(没有重复)