返回博客

动态数据脱敏:6 种模式全解析

coomia-dip 的动态数据脱敏引擎支持 6 种脱敏模式——完全遮蔽、部分遮蔽、哈希替换、区间泛化、格式保留加密和条件脱敏。脱敏策略基于 ABAC 属性评估实时生效,在查询返回阶段对敏感字段进行透明变换,无需修改底层数据。本文从脱敏架构设计、6 种模式的实现细节、性能优化到生产最佳实践,完整解析这一企业级数据保护能力。

Coomia发布于 2025年9月19日19 分钟阅读
分享本文Twitter / X

系列:S6 平台工程 · 第 6 篇 | 难度:高级 | 阅读时间:18 分钟

动态数据脱敏:6 种模式全解析

#TL;DR

coomia-dip 的动态数据脱敏引擎支持 6 种脱敏模式——完全遮蔽、部分遮蔽、哈希替换、区间泛化、格式保留加密和条件脱敏。脱敏策略基于 ABAC 属性评估实时生效,在查询返回阶段对敏感字段进行透明变换,无需修改底层数据。本文从脱敏架构设计、6 种模式的实现细节、性能优化到生产最佳实践,完整解析这一企业级数据保护能力。

#1. 为什么需要动态数据脱敏

#1.1 静态脱敏的局限

传统的静态脱敏(Static Data Masking)在数据导出或复制时一次性替换敏感信息。这种方式存在根本性缺陷:

  • 数据副本膨胀:每个使用场景需要一份独立的脱敏副本,存储成本线性增长
  • 时效性差:原始数据更新后,脱敏副本需要重新生成
  • 权限不可变:脱敏策略固化在副本中,无法根据查询者身份动态调整
  • 审计盲区:无法追踪谁在什么时间查看了什么级别的脱敏数据

#1.2 动态脱敏的核心优势

动态数据脱敏(Dynamic Data Masking, DDM)在查询返回路径上实时执行脱敏变换:

Code
用户查询 → PolicyEngine评估 → 查询执行 → 脱敏变换 → 返回结果
                |                              |
          基于ABAC属性                    基于字段策略
          确定脱敏级别                    应用脱敏模式

核心优势包括:

  • 零副本:原始数据只有一份,脱敏在查询路径上实时计算
  • 身份感知:同一字段对不同角色展示不同的脱敏级别
  • 策略即时生效:脱敏策略变更后立即生效,无需重建数据
  • 完整审计:每次脱敏操作都记录在审计日志中

#1.3 对标 Palantir Foundry

能力Palantir Foundrycoomia-dip
动态脱敏内置但规则有限6 种脱敏模式
策略驱动Marking 级别ABAC 属性驱动
格式保留部分支持FPE 完整实现
条件脱敏不支持支持上下文条件
性能影响不透明<5% 查询延迟增加

#2. 脱敏架构设计

#2.1 系统架构

coomia-dip 的动态脱敏引擎作为查询结果的后处理器,嵌入在数据返回路径中:

Code
┌─────────────────────────────────────────────────┐
│                   API Gateway                    │
│              (gRPC / REST 入口)                   │
└──────────────────────┬──────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────┐
│              Query Execution Engine              │
│           (Iceberg + Nessie 查询)                │
└──────────────────────┬──────────────────────────┘
                       │ 原始结果集
┌──────────────────────▼──────────────────────────┐
│            Dynamic Masking Engine                │
│  ┌─────────┐  ┌──────────┐  ┌───────────────┐  │
│  │ Policy   │  │ Field    │  │ Masking       │  │
│  │ Resolver │→│ Classifier│→│ Transformer   │  │
│  └─────────┘  └──────────┘  └───────────────┘  │
│       ↑              ↑              │           │
│  ABAC Engine    Schema Registry    6种模式      │
└──────────────────────┬──────────────────────────┘
                       │ 脱敏后结果集
┌──────────────────────▼──────────────────────────┐
│              Response Serializer                 │
│           (gRPC Protobuf 序列化)                  │
└─────────────────────────────────────────────────┘

#2.2 核心组件

脱敏引擎由三个核心组件协作:

Python
class DynamicMaskingEngine:
    """动态脱敏引擎 - 查询结果后处理器"""

    def __init__(
        self,
        policy_resolver: PolicyResolver,
        field_classifier: FieldClassifier,
        masking_registry: MaskingTransformerRegistry,
    ):
        self._policy_resolver = policy_resolver
        self._field_classifier = field_classifier
        self._masking_registry = masking_registry

    async def apply_masking(
        self,
        result_set: ResultSet,
        request_context: RequestContext,
    ) -> ResultSet:
        """对查询结果集应用动态脱敏"""
        # 1. 解析当前用户的脱敏策略
        policies = await self._policy_resolver.resolve(
            subject=request_context.subject,
            resource=result_set.source_object,
            environment=request_context.environment,
        )

        # 2. 对每个字段确定脱敏模式
        field_masks = {}
        for field in result_set.schema.fields:
            classification = self._field_classifier.classify(field)
            mask_mode = policies.get_mask_mode(classification)
            if mask_mode:
                field_masks[field.name] = mask_mode

        # 3. 应用脱敏变换
        masked_result = result_set.copy()
        for row in masked_result.rows:
            for field_name, mode in field_masks.items():
                transformer = self._masking_registry.get(mode)
                row[field_name] = transformer.transform(
                    value=row[field_name],
                    field_name=field_name,
                    context=request_context,
                )

        return masked_result

#2.3 策略解析流程

PolicyResolver 连接 ABAC 引擎,根据主体属性、资源属性和环境属性确定脱敏级别:

Python
class PolicyResolver:
    """脱敏策略解析器"""

    async def resolve(
        self,
        subject: Subject,
        resource: OntologyObject,
        environment: Environment,
    ) -> MaskingPolicies:
        """解析适用的脱敏策略"""
        # 评估 ABAC 属性
        evaluation = await self._abac_engine.evaluate(
            subject_attrs={
                "roles": subject.roles,
                "clearance_level": subject.clearance_level,
                "department": subject.department,
            },
            resource_attrs={
                "classification": resource.classification,
                "sensitivity": resource.sensitivity_label,
                "data_domain": resource.data_domain,
            },
            environment_attrs={
                "time": environment.request_time,
                "network": environment.network_zone,
                "client_type": environment.client_type,
            },
        )

        return MaskingPolicies.from_evaluation(evaluation)

#3. 六种脱敏模式详解

#3.1 模式一:完全遮蔽(Full Masking)

完全遮蔽是最严格的脱敏模式,将字段值完全替换为固定掩码字符:

Python
class FullMaskingTransformer(MaskingTransformer):
    """完全遮蔽 - 将字段值替换为掩码字符"""

    mode = MaskingMode.FULL

    def transform(
        self,
        value: Any,
        field_name: str,
        context: RequestContext,
    ) -> str:
        if value is None:
            return None

        # 根据字段类型选择掩码
        if isinstance(value, str):
            return "***" if len(value) <= 10 else "******"
        elif isinstance(value, (int, float)):
            return 0
        elif isinstance(value, datetime):
            return datetime(1970, 1, 1)
        else:
            return "***"

适用场景

  • 最高机密数据(如密码哈希、密钥)
  • 对无权限用户隐藏整个字段值
  • 数据分类等级为 D(绝密)的字段

示例

原始值脱敏后
张三丰***
13812345678******
2024-01-151970-01-01

#3.2 模式二:部分遮蔽(Partial Masking)

部分遮蔽保留字段值的部分结构信息,通过配置保留前缀和后缀长度:

Python
class PartialMaskingTransformer(MaskingTransformer):
    """部分遮蔽 - 保留前后缀,中间用掩码替换"""

    mode = MaskingMode.PARTIAL

    def __init__(self, prefix_len: int = 3, suffix_len: int = 4, mask_char: str = "*"):
        self._prefix_len = prefix_len
        self._suffix_len = suffix_len
        self._mask_char = mask_char

    def transform(self, value: Any, field_name: str, context: RequestContext) -> str:
        if value is None:
            return None

        s = str(value)
        if len(s) <= self._prefix_len + self._suffix_len:
            return self._mask_char * len(s)

        prefix = s[:self._prefix_len]
        suffix = s[-self._suffix_len:]
        masked_len = len(s) - self._prefix_len - self._suffix_len
        return f"{prefix}{self._mask_char * masked_len}{suffix}"

字段特定配置

Python
FIELD_MASKING_PROFILES = {
    "phone": PartialMaskingTransformer(prefix_len=3, suffix_len=4),
    "email": EmailMaskingTransformer(),  # user → u***r
    "id_card": PartialMaskingTransformer(prefix_len=6, suffix_len=4),
    "bank_card": PartialMaskingTransformer(prefix_len=4, suffix_len=4),
    "name": PartialMaskingTransformer(prefix_len=1, suffix_len=0),
}

示例

字段原始值脱敏后
手机号13812345678138****5678
邮箱user@example.comu***r@example.com
身份证110101199001011234110101********1234
银行卡62220212345678906222********7890

#3.3 模式三:哈希替换(Hash Replacement)

哈希替换使用确定性哈希函数将原始值映射为固定长度的伪随机值,保留数据的统计可分析性:

Python
class HashReplacementTransformer(MaskingTransformer):
    """哈希替换 - 使用 HMAC-SHA256 生成确定性替代值"""

    mode = MaskingMode.HASH

    def __init__(self, secret_key: bytes, output_format: str = "hex16"):
        self._secret_key = secret_key
        self._output_format = output_format

    def transform(self, value: Any, field_name: str, context: RequestContext) -> str:
        if value is None:
            return None

        # 使用 HMAC 确保相同输入产生相同输出(可 JOIN)
        mac = hmac.new(
            self._secret_key,
            msg=f"{field_name}:{value}".encode("utf-8"),
            digestmod=hashlib.sha256,
        )

        digest = mac.hexdigest()

        if self._output_format == "hex16":
            return digest[:16]
        elif self._output_format == "uuid":
            return str(uuid.UUID(digest[:32]))
        else:
            return digest

核心特性

  • 确定性:相同输入总是产生相同输出,支持 JOIN 和 GROUP BY
  • 不可逆:HMAC 确保无法从哈希值反推原始值
  • 隔离性:不同字段使用不同的盐(field_name 作为前缀),防止跨字段关联
  • 密钥轮换:支持定期更换 secret_key,旧哈希值自动失效

适用场景

  • 数据分析需要统计但不需要看到原始值
  • 跨表 JOIN 保持引用完整性
  • 数据科学团队使用的沙箱环境

#3.4 模式四:区间泛化(Range Generalization)

区间泛化将精确数值替换为包含该值的区间,适用于数值和日期类型:

Python
class RangeGeneralizationTransformer(MaskingTransformer):
    """区间泛化 - 将精确值替换为区间范围"""

    mode = MaskingMode.RANGE

    def __init__(self, ranges: list[tuple] | None = None, step: int | None = None):
        self._ranges = ranges
        self._step = step

    def transform(self, value: Any, field_name: str, context: RequestContext) -> str:
        if value is None:
            return None

        if isinstance(value, (int, float)):
            return self._generalize_number(value)
        elif isinstance(value, datetime):
            return self._generalize_date(value)
        elif isinstance(value, date):
            return self._generalize_date(datetime.combine(value, datetime.min.time()))
        else:
            return str(value)

    def _generalize_number(self, value: float) -> str:
        if self._ranges:
            for low, high in self._ranges:
                if low <= value < high:
                    return f"{low}-{high}"
            return "其他"

        if self._step:
            lower = (value // self._step) * self._step
            upper = lower + self._step
            return f"{int(lower)}-{int(upper)}"

        # 默认按数量级泛化
        magnitude = 10 ** max(0, len(str(int(abs(value)))) - 1)
        lower = (value // magnitude) * magnitude
        upper = lower + magnitude
        return f"{int(lower)}-{int(upper)}"

    def _generalize_date(self, value: datetime) -> str:
        # 泛化到月份级别
        return value.strftime("%Y-%m")

示例

字段原始值泛化后
年龄3230-40
薪资2850020000-30000
日期2024-03-152024-03
交易金额1523.451000-2000

#3.5 模式五:格式保留加密(Format-Preserving Encryption)

格式保留加密(FPE)使用加密算法对原始值进行变换,但保持输出与输入相同的格式和长度。这是最复杂也最强大的脱敏模式:

Python
class FPETransformer(MaskingTransformer):
    """格式保留加密 - FF1/FF3-1 算法实现"""

    mode = MaskingMode.FPE

    def __init__(self, key: bytes, tweak: bytes):
        self._cipher = FF3Cipher(
            key=key.hex(),
            tweak=tweak.hex(),
            radix=10,
        )
        self._alpha_cipher = FF3Cipher(
            key=key.hex(),
            tweak=tweak.hex(),
            radix=36,
        )

    def transform(self, value: Any, field_name: str, context: RequestContext) -> str:
        if value is None:
            return None

        s = str(value)

        # 分离格式字符和有效字符
        format_map = []
        effective_chars = []
        for i, ch in enumerate(s):
            if ch.isdigit():
                effective_chars.append(ch)
                format_map.append(("digit", i))
            elif ch.isalpha():
                effective_chars.append(ch.lower())
                format_map.append(("alpha", i))
            else:
                format_map.append(("literal", i, ch))

        # 对有效字符进行 FPE 加密
        if all(fc[0] == "digit" for fc in format_map if fc[0] != "literal"):
            encrypted = self._cipher.encrypt("".join(effective_chars))
        else:
            encrypted = self._alpha_cipher.encrypt("".join(effective_chars))

        # 按原始格式重组
        result = list(s)
        enc_idx = 0
        for fm in format_map:
            if fm[0] in ("digit", "alpha"):
                result[fm[1]] = encrypted[enc_idx]
                enc_idx += 1

        return "".join(result)

示例

字段原始值FPE 后格式保持
手机号138-1234-5678247-8391-0562格式完全一致
身份证11010119900101123434050219870605987118 位数字
信用卡4111-1111-1111-11115392-8477-2610-3845Luhn 校验可选

FPE 的独特优势

  • 下游系统无需修改即可处理脱敏后的数据
  • 数据格式验证规则仍然有效
  • 可用于需要保持数据结构一致性的测试环境

#3.6 模式六:条件脱敏(Conditional Masking)

条件脱敏是最灵活的模式,根据运行时上下文动态选择脱敏策略:

Python
class ConditionalMaskingTransformer(MaskingTransformer):
    """条件脱敏 - 基于上下文条件动态选择脱敏策略"""

    mode = MaskingMode.CONDITIONAL

    def __init__(self, rules: list[ConditionalRule]):
        self._rules = rules

    def transform(self, value: Any, field_name: str, context: RequestContext) -> Any:
        if value is None:
            return None

        for rule in self._rules:
            if rule.evaluate(context):
                return rule.transformer.transform(value, field_name, context)

        # 无规则匹配时使用完全遮蔽(安全默认)
        return FullMaskingTransformer().transform(value, field_name, context)


class ConditionalRule:
    """条件规则"""

    def __init__(
        self,
        condition: Callable[[RequestContext], bool],
        transformer: MaskingTransformer,
        description: str,
    ):
        self.condition = condition
        self.transformer = transformer
        self.description = description

    def evaluate(self, context: RequestContext) -> bool:
        return self.condition(context)

条件规则示例

Python
phone_masking = ConditionalMaskingTransformer(rules=[
    # 规则 1:数据所有者可以看到完整值
    ConditionalRule(
        condition=lambda ctx: ctx.subject.id == ctx.resource_owner_id,
        transformer=NoOpTransformer(),
        description="数据所有者无需脱敏",
    ),
    # 规则 2:客服人员看到部分遮蔽
    ConditionalRule(
        condition=lambda ctx: "customer_service" in ctx.subject.roles,
        transformer=PartialMaskingTransformer(prefix_len=3, suffix_len=4),
        description="客服看到部分手机号",
    ),
    # 规则 3:分析师看到哈希值
    ConditionalRule(
        condition=lambda ctx: "analyst" in ctx.subject.roles,
        transformer=HashReplacementTransformer(secret_key=ANALYST_KEY),
        description="分析师看到哈希替代值",
    ),
    # 规则 4:外部API用户在非工作时间完全遮蔽
    ConditionalRule(
        condition=lambda ctx: (
            ctx.environment.client_type == "external_api"
            and not ctx.environment.is_business_hours
        ),
        transformer=FullMaskingTransformer(),
        description="外部API非工作时间完全遮蔽",
    ),
])

#4. 脱敏策略配置

#4.1 策略定义模型

脱敏策略通过 Pydantic 模型定义,支持声明式配置:

Python
class MaskingPolicy(BaseModel):
    """脱敏策略定义"""

    id: str = Field(description="策略唯一标识")
    name: str = Field(description="策略名称")
    description: str = Field(default="", description="策略描述")

    # 作用范围
    target: MaskingTarget = Field(description="脱敏目标")

    # 脱敏模式
    mode: MaskingMode = Field(description="脱敏模式")
    mode_config: dict = Field(default_factory=dict, description="模式配置参数")

    # 应用条件
    conditions: list[PolicyCondition] = Field(
        default_factory=list,
        description="应用条件(AND 关系)",
    )

    # 优先级(数值越小优先级越高)
    priority: int = Field(default=100, description="策略优先级")

    # 生效时间
    effective_from: datetime | None = Field(default=None)
    effective_until: datetime | None = Field(default=None)

    # 启用状态
    enabled: bool = Field(default=True)


class MaskingTarget(BaseModel):
    """脱敏目标"""

    object_types: list[str] = Field(
        default_factory=lambda: ["*"],
        description="适用的 Ontology 对象类型",
    )
    field_patterns: list[str] = Field(
        description="字段名匹配模式(支持通配符)",
    )
    classification_levels: list[str] = Field(
        default_factory=list,
        description="适用的数据分类等级",
    )

#4.2 策略注册与加载

Python
class MaskingPolicyRegistry:
    """脱敏策略注册表"""

    def __init__(self):
        self._policies: dict[str, MaskingPolicy] = {}
        self._compiled_cache: dict[str, CompiledPolicy] = {}

    def register(self, policy: MaskingPolicy) -> None:
        self._policies[policy.id] = policy
        self._invalidate_cache()

    def resolve_for_field(
        self,
        object_type: str,
        field_name: str,
        classification: str,
        context: RequestContext,
    ) -> MaskingTransformer | None:
        """解析字段适用的脱敏变换器"""
        applicable = []
        for policy in self._policies.values():
            if not policy.enabled:
                continue
            if not self._matches_target(policy.target, object_type, field_name, classification):
                continue
            if not self._matches_conditions(policy.conditions, context):
                continue
            if not self._is_effective(policy):
                continue
            applicable.append(policy)

        if not applicable:
            return None

        # 按优先级排序,取最高优先级
        applicable.sort(key=lambda p: p.priority)
        best = applicable[0]

        return self._create_transformer(best.mode, best.mode_config)

#5. 性能优化

#5.1 字段级缓存

脱敏策略解析的结果在单个查询生命周期内缓存,避免重复评估:

Python
class CachedMaskingEngine(DynamicMaskingEngine):
    """带缓存的脱敏引擎"""

    async def apply_masking(
        self,
        result_set: ResultSet,
        request_context: RequestContext,
    ) -> ResultSet:
        # 构建缓存键:用户 + 资源 + 环境哈希
        cache_key = self._build_cache_key(request_context)

        # 尝试从缓存获取字段脱敏映射
        field_masks = self._field_mask_cache.get(cache_key)
        if field_masks is None:
            field_masks = await self._resolve_field_masks(result_set, request_context)
            self._field_mask_cache.set(cache_key, field_masks, ttl=300)

        # 批量应用脱敏
        return self._batch_transform(result_set, field_masks)

#5.2 向量化脱敏

对于大结果集,使用向量化操作提升性能:

Python
class VectorizedMaskingEngine:
    """向量化脱敏引擎 - 使用 Arrow/Pandas 批量处理"""

    def transform_column(
        self,
        column: pa.Array,
        transformer: MaskingTransformer,
        context: RequestContext,
    ) -> pa.Array:
        """向量化处理整列数据"""
        if transformer.mode == MaskingMode.FULL:
            # 完全遮蔽:直接创建常量数组
            return pa.array(["***"] * len(column), type=pa.string())

        elif transformer.mode == MaskingMode.PARTIAL:
            # 部分遮蔽:使用 Arrow compute 函数
            values = column.to_pylist()
            masked = [transformer.transform(v, "", context) for v in values]
            return pa.array(masked, type=pa.string())

        elif transformer.mode == MaskingMode.HASH:
            # 哈希替换:批量计算
            values = column.to_pylist()
            masked = [transformer.transform(v, "", context) for v in values]
            return pa.array(masked, type=pa.string())

        return column

#5.3 性能基准

在标准测试环境下(100 万行、20 列、5 列需脱敏)的性能数据:

脱敏模式单行延迟吞吐量内存开销
完全遮蔽0.1μs10M 行/秒~0
部分遮蔽0.8μs1.25M 行/秒<1MB
哈希替换2.5μs400K 行/秒<1MB
区间泛化0.3μs3.3M 行/秒<1MB
FPE15μs67K 行/秒~5MB
条件脱敏1-16μs视规则<2MB

#6. 与权限系统的集成

#6.1 ABAC 属性驱动脱敏

脱敏策略与 ABAC 属性评估深度集成,实现基于用户画像的差异化脱敏:

Python
class ABACDrivenMaskingResolver:
    """基于 ABAC 的脱敏策略解析器"""

    CLEARANCE_MASKING_MAP = {
        # 用户安全等级 → 数据分类等级 → 脱敏模式
        ("PUBLIC", "A1"): MaskingMode.NONE,
        ("PUBLIC", "A2"): MaskingMode.PARTIAL,
        ("PUBLIC", "B"): MaskingMode.FULL,
        ("PUBLIC", "C"): MaskingMode.FULL,
        ("PUBLIC", "D"): MaskingMode.FULL,

        ("INTERNAL", "A1"): MaskingMode.NONE,
        ("INTERNAL", "A2"): MaskingMode.NONE,
        ("INTERNAL", "B"): MaskingMode.PARTIAL,
        ("INTERNAL", "C"): MaskingMode.HASH,
        ("INTERNAL", "D"): MaskingMode.FULL,

        ("CONFIDENTIAL", "A1"): MaskingMode.NONE,
        ("CONFIDENTIAL", "A2"): MaskingMode.NONE,
        ("CONFIDENTIAL", "B"): MaskingMode.NONE,
        ("CONFIDENTIAL", "C"): MaskingMode.PARTIAL,
        ("CONFIDENTIAL", "D"): MaskingMode.HASH,

        ("SECRET", "A1"): MaskingMode.NONE,
        ("SECRET", "A2"): MaskingMode.NONE,
        ("SECRET", "B"): MaskingMode.NONE,
        ("SECRET", "C"): MaskingMode.NONE,
        ("SECRET", "D"): MaskingMode.PARTIAL,
    }

#6.2 审计集成

每次脱敏操作都生成审计事件,记录脱敏的详细信息:

Python
class MaskingAuditEvent(BaseModel):
    """脱敏审计事件"""

    event_type: str = "DATA_MASKING"
    timestamp: datetime
    subject_id: str
    resource_id: str
    object_type: str
    fields_masked: list[FieldMaskingDetail]
    masking_policy_ids: list[str]
    query_id: str
    result_row_count: int

class FieldMaskingDetail(BaseModel):
    field_name: str
    masking_mode: MaskingMode
    classification_level: str
    original_type: str

#7. 测试策略

#7.1 脱敏正确性测试

Python
class TestDynamicMasking:
    """脱敏正确性测试"""

    def test_full_masking_hides_all_content(self):
        transformer = FullMaskingTransformer()
        assert transformer.transform("sensitive_data", "field", ctx) == "******"
        assert transformer.transform(12345, "field", ctx) == 0

    def test_partial_masking_preserves_structure(self):
        transformer = PartialMaskingTransformer(prefix_len=3, suffix_len=4)
        result = transformer.transform("13812345678", "phone", ctx)
        assert result == "138****5678"
        assert len(result) == 11  # 长度保持

    def test_hash_deterministic(self):
        transformer = HashReplacementTransformer(secret_key=b"test")
        r1 = transformer.transform("value", "field", ctx)
        r2 = transformer.transform("value", "field", ctx)
        assert r1 == r2  # 相同输入相同输出

    def test_hash_different_fields_different_output(self):
        transformer = HashReplacementTransformer(secret_key=b"test")
        r1 = transformer.transform("value", "field_a", ctx)
        r2 = transformer.transform("value", "field_b", ctx)
        assert r1 != r2  # 不同字段不同输出

    def test_fpe_preserves_format(self):
        transformer = FPETransformer(key=b"0" * 16, tweak=b"0" * 8)
        result = transformer.transform("138-1234-5678", "phone", ctx)
        # 格式保持:3位-4位-4位
        assert len(result) == 13
        assert result[3] == "-"
        assert result[8] == "-"

    def test_conditional_masking_role_based(self):
        owner_ctx = make_context(subject_id="owner_1", resource_owner="owner_1")
        analyst_ctx = make_context(roles=["analyst"])

        result_owner = phone_masking.transform("13812345678", "phone", owner_ctx)
        result_analyst = phone_masking.transform("13812345678", "phone", analyst_ctx)

        assert result_owner == "13812345678"  # 所有者看到原始值
        assert len(result_analyst) == 16  # 分析师看到哈希值

#7.2 性能回归测试

Python
@pytest.mark.benchmark
def test_masking_performance_under_sla(benchmark):
    """确保脱敏延迟在 SLA 范围内"""
    engine = DynamicMaskingEngine(...)
    result_set = generate_result_set(rows=10000, cols=20, masked_cols=5)

    result = benchmark(engine.apply_masking, result_set, test_context)

    # SLA: <5% 查询延迟增加
    assert benchmark.stats["mean"] < 0.05  # 50ms for 10K rows

#8. 生产最佳实践

#8.1 策略设计原则

  1. 最小暴露原则:默认脱敏,仅对明确授权的用户降低脱敏级别
  2. 分层策略:全局策略 → 对象类型策略 → 字段策略,优先级依次递增
  3. 安全默认:无匹配策略时使用完全遮蔽
  4. 可审计:每次脱敏操作都记录审计日志

#8.2 性能调优建议

  • 对高频查询启用字段级脱敏缓存
  • FPE 模式仅用于需要格式保留的场景,其他场景优先使用部分遮蔽
  • 大结果集(>10 万行)使用向量化引擎
  • 脱敏策略变更后主动清理缓存

#8.3 合规性映射

法规要求脱敏模式说明
GDPR 匿名化哈希替换确定性伪匿名
GDPR 数据最小化区间泛化保留分析价值
中国《个人信息保护法》部分遮蔽保留部分信息
PCI DSS 卡号保护FPE 或部分遮蔽格式保留
HIPAA 去标识化哈希替换 + 区间泛化Safe Harbor 方法

#9. 总结

coomia-dip 的动态脱敏引擎通过 6 种互补的脱敏模式,覆盖了从最严格的完全遮蔽到最灵活的条件脱敏的全部场景。关键设计决策包括:

  1. 模式丰富性:6 种模式覆盖所有企业级脱敏需求
  2. 策略驱动:与 ABAC 深度集成,基于用户画像自动选择脱敏级别
  3. 零侵入:在查询返回路径透明执行,业务代码无感知
  4. 高性能:通过缓存和向量化优化,脱敏延迟控制在 5% 以内
  5. 可审计:每次脱敏操作都记录完整的审计日志

下一篇将深入探讨 coomia-dip 的 7 级数据分类体系,它为脱敏引擎提供了关键的元数据输入。