在数据脱敏领域,假名化(Pseudonymization) 是一种通过替换真实标识符(如姓名、用户ID、手机号)为“假名”(虚假但符合业务逻辑的标识符),以隐藏数据主体真实身份的技术。与简单的字符替换(如用*
隐藏手机号中间四位)不同,假名化的核心特点是保持数据关联性——同一原始数据在不同场景下始终被替换为同一个假名,确保脱敏后的数据仍可用于统计分析、测试验证等需要关联关系的场景。
用无意义或虚构的标识符(假名)替换真实敏感标识符,例如将用户真实姓名“张三”替换为“李华”,用户ID“10086”替换为“user_7a3f2d”。
同一原始值始终对应同一假名。例如,“张三”在所有数据集中都被替换为“李华”,而非随机生成不同的名字,这样可以保留数据的关联关系(如同一用户的订单记录、行为日志仍可通过“李华”关联分析)。
假名需符合原始数据的格式和业务逻辑。例如:
假名化≠匿名化:
实现假名化的关键是维护“原始值→假名”的映射表,确保同一原始值始终生成相同假名。具体步骤:
需安装faker
库(生成符合逻辑的假名):
pip install faker
下面封装一个Pseudonymizer
类,支持姓名、用户ID、邮箱的假名化,并提供映射表的保存/加载功能:
import re
import random
import json
from faker import Faker
from typing import Dict, Optional
class Pseudonymizer:
def __init__(self, mapping_file: str = "pseudo_mappings.json"):
"""
初始化假名化工具
:param mapping_file: 映射表保存路径(JSON格式)
"""
self.fake = Faker("zh_CN") # 生成中文假数据
self.mapping_file = mapping_file
# 初始化映射表(原始值→假名)
self.mappings = {
"name": {}, # 姓名映射:{"张三": "李华", ...}
"user_id": {}, # 用户ID映射:{"10086": "user_7a3f2d", ...}
"email": {} # 邮箱映射:{"[email protected]": "[email protected]", ...}
}
# 尝试加载已保存的映射表
self.load_mappings()
def _generate_pseudo_name(self) -> str:
"""生成假姓名(中文,确保不包含特殊字符)"""
return self.fake.name()
def _generate_pseudo_user_id(self) -> str:
"""生成假用户ID(格式:user_ + 6位随机字母数字)"""
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
suffix = "".join(random.choices(chars, k=6))
return f"user_{suffix}"
def _generate_pseudo_email(self, name: str) -> str:
"""基于假名生成假邮箱(格式:姓名拼音/缩写 + 随机数@域名)"""
# 简化处理:姓名转拼音(实际可结合pinyin库,此处用随机字符串模拟)
username = re.sub(r"[^\w]", "", name.lower()) + str(random.randint(100, 999))
domains = ["test.com", "demo.cn", "fake.org", "pseudo.net"]
return f"{username}@{random.choice(domains)}"
def pseudonymize_name(self, real_name: str) -> str:
"""姓名假名化:同一真实姓名→同一假名"""
real_name = real_name.strip()
if not real_name:
return ""
# 查映射表,存在则直接返回
if real_name in self.mappings["name"]:
return self.mappings["name"][real_name]
# 不存在则生成新假名并更新映射表
pseudo_name = self._generate_pseudo_name()
# 避免生成重复假名(理论概率低,实际可加循环校验)
while pseudo_name in self.mappings["name"].values():
pseudo_name = self._generate_pseudo_name()
self.mappings["name"][real_name] = pseudo_name
return pseudo_name
def pseudonymize_user_id(self, real_user_id: str) -> str:
"""用户ID假名化:同一真实ID→同一假ID"""
real_user_id = str(real_user_id).strip()
if not real_user_id:
return ""
if real_user_id in self.mappings["user_id"]:
return self.mappings["user_id"][real_user_id]
pseudo_id = self._generate_pseudo_user_id()
while pseudo_id in self.mappings["user_id"].values():
pseudo_id = self._generate_pseudo_user_id()
self.mappings["user_id"][real_user_id] = pseudo_id
return pseudo_id
def pseudonymize_email(self, real_email: str, pseudo_name: Optional[str] = None) -> str:
"""邮箱假名化:同一真实邮箱→同一假邮箱,可结合假名姓名生成"""
real_email = real_email.strip()
if not real_email:
return ""
if real_email in self.mappings["email"]:
return self.mappings["email"][real_email]
# 若提供假名姓名,基于姓名生成邮箱;否则随机生成
if pseudo_name:
pseudo_email = self._generate_pseudo_email(pseudo_name)
else:
pseudo_email = self.fake.email()
while pseudo_email in self.mappings["email"].values():
pseudo_email = self._generate_pseudo_email(pseudo_name) if pseudo_name else self.fake.email()
self.mappings["email"][real_email] = pseudo_email
return pseudo_email
def save_mappings(self) -> None:
"""保存映射表到JSON文件(持久化,避免程序重启后映射丢失)"""
with open(self.mapping_file, "w", encoding="utf-8") as f:
json.dump(self.mappings, f, ensure_ascii=False, indent=2)
print(f"映射表已保存到 {self.mapping_file}")
def load_mappings(self) -> None:
"""从JSON文件加载映射表(恢复历史映射关系)"""
try:
with open(self.mapping_file, "r", encoding="utf-8") as f:
self.mappings = json.load(f)
print(f"从 {self.mapping_file} 加载映射表成功")
except FileNotFoundError:
print(f"映射表文件 {self.mapping_file} 不存在,将使用空映射表")
except Exception as e:
print(f"加载映射表失败:{e},将使用空映射表")
下面用一组真实用户数据演示假名化效果,重点验证“一致性映射”和“业务特征保留”:
if __name__ == "__main__":
# 初始化假名化工具(映射表保存到pseudo_mappings.json)
pseudo = Pseudonymizer(mapping_file="pseudo_mappings.json")
# 原始敏感数据(包含姓名、用户ID、邮箱)
raw_users = [
{"name": "张三", "user_id": "1001", "email": "[email protected]"},
{"name": "李四", "user_id": "1002", "email": "[email protected]"},
{"name": "张三", "user_id": "1001", "email": "[email protected]"}, # 重复数据,验证一致性
{"name": "王五", "user_id": "1003", "email": "[email protected]"}
]
# 对原始数据进行假名化处理
pseudo_users = []
for user in raw_users:
# 姓名假名化
pseudo_name = pseudo.pseudonymize_name(user["name"])
# 用户ID假名化
pseudo_uid = pseudo.pseudonymize_user_id(user["user_id"])
# 邮箱假名化(结合假名姓名生成,增强真实性)
pseudo_email = pseudo.pseudonymize_email(user["email"], pseudo_name=pseudo_name)
pseudo_users.append({
"原始姓名": user["name"],
"假名姓名": pseudo_name,
"原始用户ID": user["user_id"],
"假名用户ID": pseudo_uid,
"原始邮箱": user["email"],
"假名邮箱": pseudo_email
})
# 打印处理结果
print("=== 原始数据 vs 假名化数据 ===")
for i, pu in enumerate(pseudo_users, 1):
print(f"\n用户 {i}:")
for key, value in pu.items():
print(f"{key}: {value}")
# 保存映射表(下次运行可恢复相同映射)
pseudo.save_mappings()
运行上述代码,输出示例如下(因Faker随机生成,具体假名可能不同,但核心规律一致):
从 pseudo_mappings.json 加载映射表成功
=== 原始数据 vs 假名化数据 ===
用户 1:
原始姓名: 张三
假名姓名: 刘芳
原始用户ID: 1001
假名用户ID: user_x7f2k9
原始邮箱: [email protected]
假名邮箱: 刘芳[email protected]
用户 2:
原始姓名: 李四
假名姓名: 王强
原始用户ID: 1002
假名用户ID: user_b8d1m3
原始邮箱: [email protected]
假名邮箱: 王强[email protected]
用户 3:
原始姓名: 张三
假名姓名: 刘芳 # 同一原始姓名→同一假名
原始用户ID: 1001
假名用户ID: user_x7f2k9 # 同一原始ID→同一假名ID
原始邮箱: [email protected]
假名邮箱: 刘芳[email protected] # 同一原始邮箱→同一假名邮箱
用户 4:
原始姓名: 王五
假名姓名: 赵敏
原始用户ID: 1003
假名用户ID: user_p9e4s2
原始邮箱: [email protected]
假名邮箱: 赵敏[email protected]
映射表已保存到 pseudo_mappings.json
假名化的安全性依赖于映射表(原始值→假名)的保护。若映射表泄露,攻击者可还原原始数据。建议:
生成的假名需确保不与真实世界中的标识符重复(例如,假名“张三”可能对应真实用户,导致混淆)。可通过以下方式避免:
while
循环检查是否已存在于映射表值中)。根据GDPR等法规,假名化数据仍可能被视为“个人数据”(因为可通过映射表恢复)。若需完全合规,需结合其他技术(如数据匿名化、访问控制),或明确数据用途和存储期限。
参考文章