哪些数据不应该直接喂给模型?
将数据发送给第三方 AI 服务(如 OpenAI、Anthropic)时,数据会经过对方服务器处理,存在隐私泄露和合规风险。以下类别的数据必须在发送前脱敏处理:
| 数据类别 | 具体示例 | 风险等级 |
|---|---|---|
| 个人身份信息(PII) | 姓名、身份证号、手机号、邮箱、地址 | 🔴 极高 |
| 认证凭证 | 密码、API Key、Token、私钥、Cookie | 🔴 极高 |
| 金融数据 | 银行卡号、信用卡号、账户余额、交易流水 | 🔴 极高 |
| 医疗健康数据 | 诊断记录、用药信息、基因数据 | 🔴 极高 |
| 商业机密 | 未发布产品计划、客户列表、定价策略 | 🟠 高 |
| 内部系统信息 | 服务器IP、数据库结构、内部接口文档 | 🟠 高 |
| 员工信息 | 薪资、绩效、人事档案 | 🟡 中 |
三种脱敏方法
方法一:正则表达式替换
适合结构化、格式固定的敏感数据,如手机号、身份证号、银行卡号等。速度快,无需额外依赖。
import re
from typing import Tuple
# 脱敏规则配置
DESENSITIZE_RULES = [
# (模式名, 正则表达式, 替换值)
("phone", r'1[3-9]\d{9}', "[手机号]"),
("id_card", r'\d{17}[\dXx]', "[身份证号]"),
("bank_card",r'\d{16,19}', "[银行卡号]"),
("email", r'[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}', "[邮箱]"),
("api_key", r'(sk|pk|api)[_-]?[a-zA-Z0-9]{20,}', "[API密钥]"),
("password", r'(password|pwd|passwd)\s*[:=]\s*\S+', "[密码已隐藏]"),
("ip", r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', "[IP地址]"),
("aws_key", r'AKIA[0-9A-Z]{16}', "[AWS密钥]"),
]
def desensitize(text: str) -> Tuple[str, dict]:
"""
对文本进行脱敏处理
返回:(脱敏后文本, 统计信息)
"""
result = text
stats = {}
for name, pattern, replacement in DESENSITIZE_RULES:
matches = re.findall(pattern, result, re.IGNORECASE)
if matches:
stats[name] = len(matches)
result = re.sub(pattern, replacement, result, flags=re.IGNORECASE)
return result, stats
# 使用示例
raw_text = """
客户张三,手机号 13812345678,
身份证 110101199001011234,
邮箱 zhang@example.com。
API Key: sk-abc123def456ghi789jkl012
"""
clean_text, report = desensitize(raw_text)
print("脱敏结果:", clean_text)
print("脱敏统计:", report)
# 输出:
# 客户张三,手机号 [手机号],
# 身份证 [身份证号],
# 邮箱 [邮箱]。
# API Key: [API密钥]
方法二:NER 命名实体识别
正则无法覆盖非结构化的实体,如人名、机构名、地址等自然语言表达的 PII。此时需要借助 NLP 模型做命名实体识别(NER):
# 使用 spacy 或 presidio 做 NER 脱敏
# pip install presidio-analyzer presidio-anonymizer
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()
def ner_desensitize(text: str, language: str = "zh") -> str:
"""使用 NER 识别并脱敏实体"""
# 识别实体
results = analyzer.analyze(
text=text,
entities=["PERSON", "PHONE_NUMBER", "EMAIL_ADDRESS",
"CREDIT_CARD", "LOCATION", "DATE_TIME"],
language=language
)
# 匿名化处理
anonymized = anonymizer.anonymize(
text=text,
analyzer_results=results
)
return anonymized.text
# 对于中文,也可使用 HanLP 或 jieba + 自定义规则
# 企业场景推荐:本地部署的轻量 NER 模型,避免数据外发
方法三:令牌化(Tokenization)映射
用可逆的令牌替换敏感数据,处理完成后可以还原。适合需要 AI 分析结果后与原始数据关联的场景:
import uuid
import json
class TokenizationVault:
"""令牌化保险库:敏感数据 ↔ 令牌 双向映射"""
def __init__(self):
self._token_to_data = {} # 令牌 → 原始数据
self._data_to_token = {} # 原始数据 → 令牌(避免重复令牌)
def tokenize(self, sensitive_value: str, prefix: str = "TOKEN") -> str:
"""将敏感值替换为令牌"""
if sensitive_value in self._data_to_token:
return self._data_to_token[sensitive_value]
token = f"[{prefix}_{uuid.uuid4().hex[:8].upper()}]"
self._token_to_data[token] = sensitive_value
self._data_to_token[sensitive_value] = token
return token
def detokenize(self, text: str) -> str:
"""将令牌还原为原始值"""
result = text
for token, original in self._token_to_data.items():
result = result.replace(token, original)
return result
def tokenize_text(self, text: str, patterns: list) -> str:
"""批量令牌化文本中的敏感数据"""
import re
result = text
for pattern, prefix in patterns:
matches = re.findall(pattern, result)
for match in matches:
token = self.tokenize(match, prefix)
result = result.replace(match, token, 1)
return result
# 使用示例
vault = TokenizationVault()
original = "请分析张三(13812345678)的合同违约问题"
tokenized = vault.tokenize_text(original, [
(r'[\u4e00-\u9fa5]{2,4}(?=()', "NAME"),
(r'1[3-9]\d{9}', "PHONE"),
])
print("发给AI:", tokenized)
# → "请分析[NAME_A1B2C3D4]([PHONE_E5F6G7H8])的合同违约问题"
ai_response = "根据分析,[NAME_A1B2C3D4] 存在合同违约行为..."
restored = vault.detokenize(ai_response)
print("还原结果:", restored)
# → "根据分析,张三 存在合同违约行为..."
脱敏架构设计建议
- 在网关层统一脱敏:在请求到达 AI 接口前,由专门的脱敏中间件处理,业务代码无感知
- 令牌保险库隔离存储:映射关系存储在独立的安全存储中,与 AI 调用链路完全隔离
- 日志脱敏:确保请求/响应日志中也不保存原始敏感数据
- 定期审计:定期抽查发送给 AI 的实际内容,确认脱敏规则未遗漏新型数据格式
- 本地模型优先:对于高度敏感的数据,优先考虑使用本地部署的模型,彻底避免数据外发
小结
数据脱敏是 AI 应用合规的第一道门。正则适合结构化数据、NER 适合非结构化实体、令牌化适合需要还原的场景。三者结合,配合网关层的统一拦截,可以构建完整的数据保护链路。记住:最好的防护是数据根本不离开你的系统,本地部署模型是终极方案。