数据脱敏中的假名化技术,用python代码实现

在数据脱敏领域,假名化(Pseudonymization) 是一种通过替换真实标识符(如姓名、用户ID、手机号)为“假名”(虚假但符合业务逻辑的标识符),以隐藏数据主体真实身份的技术。与简单的字符替换(如用*隐藏手机号中间四位)不同,假名化的核心特点是保持数据关联性——同一原始数据在不同场景下始终被替换为同一个假名,确保脱敏后的数据仍可用于统计分析、测试验证等需要关联关系的场景。
数据脱敏中的假名化技术,用python代码实现_第1张图片

一、假名化技术的核心特点

1. 标识符替换

用无意义或虚构的标识符(假名)替换真实敏感标识符,例如将用户真实姓名“张三”替换为“李华”,用户ID“10086”替换为“user_7a3f2d”。

2. 一致性映射

同一原始值始终对应同一假名。例如,“张三”在所有数据集中都被替换为“李华”,而非随机生成不同的名字,这样可以保留数据的关联关系(如同一用户的订单记录、行为日志仍可通过“李华”关联分析)。

3. 业务特征保留

假名需符合原始数据的格式和业务逻辑。例如:

  • 替换手机号时,假名仍需是11位数字且符合运营商号段规则;
  • 替换邮箱时,假名需包含“@”和域名,格式与真实邮箱一致。

4. 与匿名化的区别

假名化≠匿名化:

  • 假名化:通过映射表(原始值→假名)可恢复原始数据(需保护映射表安全),因此在GDPR等法规中仍可能被视为“个人数据”;
  • 匿名化:完全不可逆,无法通过任何手段恢复原始数据,不属于个人数据。

应用场景

  • 数据分析:保留用户行为关联性,同时隐藏真实身份;
  • 测试环境:用假名化数据模拟生产环境,确保测试时数据逻辑正确;
  • 数据共享:在企业间共享数据时,用假名保护用户隐私,同时保留数据统计价值。

二、Python实现假名化技术:核心思路与Demo

核心思路

实现假名化的关键是维护“原始值→假名”的映射表,确保同一原始值始终生成相同假名。具体步骤:

  1. 定义需要假名化的字段(如姓名、用户ID、邮箱);
  2. 对每个字段设计假名生成规则(需符合业务格式);
  3. 用字典存储映射关系(支持持久化到文件,避免重复生成);
  4. 处理数据时,先查映射表:存在则返回假名,不存在则生成新假名并更新映射表。

环境准备

需安装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
关键结论:
  1. 一致性:用户3与用户1的原始数据相同(张三、1001、[email protected]),假名化结果完全一致,验证了“同一原始值→同一假名”。
  2. 业务特征:假名姓名(刘芳、王强)是真实中文姓名,假名用户ID(user_x7f2k9)符合自定义格式,假名邮箱(刘芳[email protected])包含姓名特征和合法域名,均保留了原始数据的业务逻辑。

三、假名化技术的注意事项

1. 映射表安全管理

假名化的安全性依赖于映射表(原始值→假名)的保护。若映射表泄露,攻击者可还原原始数据。建议:

  • 加密存储映射表(如用AES加密JSON文件);
  • 严格控制映射表访问权限(仅脱敏管理员可访问)。

2. 避免假名与真实数据冲突

生成的假名需确保不与真实世界中的标识符重复(例如,假名“张三”可能对应真实用户,导致混淆)。可通过以下方式避免:

  • 设计特殊格式(如用户ID固定前缀“pseudo_”);
  • 对生成的假名进行去重校验(如Demo中while循环检查是否已存在于映射表值中)。

3. 合规性考量

根据GDPR等法规,假名化数据仍可能被视为“个人数据”(因为可通过映射表恢复)。若需完全合规,需结合其他技术(如数据匿名化、访问控制),或明确数据用途和存储期限。

参考文章

  1. 脱敏?怎么脱?看完这一篇就够了
  2. 董学耕:数据产品开发与数据脱敏
  3. 科普:数据脱敏、加密、假名化、去标识化与匿名化的区分

你可能感兴趣的:(数据安全,python,数据脱敏,假名化)