返回博客

PII 脱敏一刀切:把引流二维码当个人信息砍掉,C 端业务整体断裂

安全加固是好事,但"好意"也能砍断业务。这次踩的坑发生在一次 P0 级 PII 修复里:我们把所有"看起来跟人有关"的字段统一加了 manager-only 判断,结果上线后 C 端用户一片投诉——导购联系二维码全部消失,引流链路整体断裂。

问题根源只有一句话:企业微信"联系我"二维码根本不是 PII,它是对外公开的引流物料。

现象

修复上线后,C 端小程序的门店详情页导购联系入口全部变成空白。用户无法扫码加导购微信,业务侧反馈"客户加不上来"。后台排查接口返回正常——qrCode 字段有值,但前端拿到的始终是空字符串。

根因

PII 修复脚本在 presenter 层(序列化/DTO 层)对"联系方式类字段"做了统一处理:

// ❌ 一刀切 manager-only
if ($viewerRole === 'manager' && !empty($clerk['qrCode'])) {
    $out['qrCode'] = $clerk['qrCode'];
} else {
    $out['qrCode'] = '';   // C 端永远拿不到
}

这段代码的逻辑看似合理:只有管理员才能看到员工的联系方式。但问题在于,qrCode 存储的是企业微信"联系我"活码——这是一个面向所有公众生成的引流二维码,本质上和贴在门店海报上的二维码没有区别。把它按手机号的规格脱敏,等于把公开物料当作机密信息处理了。

坑:公开物料≠真 PII,分类错误导致业务整体断裂

企业微信"联系我"二维码、客服微信号、官网链接、产品 banner 等,是主动对外投放的引流物料,任何人扫描都是预期行为。把它们归入 manager-only 脱敏分级,会让 C 端业务功能完全失效。

排查路径:发现"接口有值但前端拿不到"时,优先检查 presenter / transformer / serializer 层是否有强制清空逻辑,再检查权限判定是否把字段错误归类。

正解

建立"字段公开度分级表",至少 3 档,不同档位对应不同可见性策略:

| 分级 | 典型字段 | 可见性 | |------|----------|--------| | 公开物料 | 企微"联系我"二维码、客服微信号、官网链接 | 任何人(含匿名)可见 | | 弱 PII | 头像、昵称、工号 | 登录用户可见 | | 真 PII | 手机号、邮箱、openid、身份证 | manager-only / self-only |

对应代码修正:

// ✅ 按字段公开度分别处理
 
// 公开物料:直接透传,无需权限判断
$out['qrCode'] = !empty($clerk['qrCode']) ? $clerk['qrCode'] : '';
 
// 弱 PII:登录可见
$out['avatar'] = !empty($clerk['avatar']) ? $clerk['avatar'] : '';
 
// 真 PII:manager-only,非 manager 时脱敏或清空
if ($viewerRole === 'manager') {
    $out['mobile'] = $clerk['mobile'];
    $out['openid'] = $clerk['openid'];
} else {
    $out['mobile'] = $this->maskMobile($clerk['mobile']);  // 138****1234
    $out['openid'] = '';
}
正解:分级脱敏,公开物料不进脱敏管道

安全 review 或渗透测试报告指出"X 字段涉及个人信息"时,先问两个问题:

  1. 用户能主动扫/访问这个内容吗? 能 → 公开物料,不做 manager-only
  2. 这个内容是否主动对外投放? 是 → 公开物料,不做 manager-only

只有明确是"仅系统内部使用、不对外暴露"的数据,才需要严格脱敏。

排查路径

遇到"接口返回字段有值,但前端始终拿到空"时,按以下顺序查:

  1. controller 返回前打日志确认 SELECT 字段已含目标字段
  2. 对比脱敏前后值,看 presenter/transformer 层是否强制清空
  3. Review 权限判定逻辑——是否存在"永远不返回 manager 角色"或"字段被错误归类为 PII"

一句话外卖

安全加固时,先问「这个字段是对外公开的物料还是需要保密的个人信息」,公开物料不进脱敏管道,否则你会用「安全」把业务功能砍掉。