CV工程师
2026-10-27 10:36:56 阅读:18 · 2497 字 · 约 7 分钟
做国内的后端系统,总会有一批不能明文出现的数据:手机号、身份证、银行卡、车牌、统一社会信用代码。日志要脱敏,接口返回要脱敏,导出报表也要脱敏。自己写正则替换当然可行,但规则零散、边界多——身份证有 15 位和 18 位、姓名要处理复姓、车牌还要考虑新能源格式。维护过一遍的人都知道有多琐碎。
mask-cn 是一个专门解决这个问题的 MIT 开源库,composer require snowmannunu/mask-cn 即可安装。它把中文本土场景的脱敏规则内置成策略,开箱即用,PHP 7.3+ 和 8.x 都支持(需要 ext-mbstring 扩展)。写作本文时最新稳定版是 v1.2.0。
所有脱敏都通过 Mask 类的静态方法调用:
use MaskCn\Mask;
Mask::phone('13812345678'); // "138****5678"
Mask::idCard('110101199003078888'); // "110101********8888"
Mask::bankCard('6222021234567890123'); // "6222 *********** 0123"
Mask::name('张三'); // "张*"
Mask::name('欧阳娜娜'); // "欧阳*娜" ← 复姓识别
Mask::name('诸葛孔明'); // "诸葛*明" ← 复姓识别
Mask::email('foo@example.com'); // "f**@example.com"
Mask::plate('京A12345'); // "京A***45"复姓识别是我觉得比较贴心的细节——直接按首位字符截断,"欧阳"会被切成"欧*",这是很多通用脱敏代码都会踩的坑。
除了这些常见的,还内置了一批证件类策略:统一社会信用代码(uscc,兼容 18 位 USCC、9 位组织机构代码和旧版营业执照)、港澳回乡证与居住证(hkMoPass)、台湾身份证与居住证(taiwanId)、护照(passport),以及保留省市行政区前缀的中文名地址脱敏:
Mask::address('广东省深圳市南山区科技园'); // "广东省深圳市******"
Mask::address('北京市朝阳区建国路'); // "北京市******"真实业务里很少单脱一个字段,更多是处理用户数组。Mask::array() 按字段名映射策略一次搞定:
$user = [
'name' => '张小明',
'phone' => '13812345678',
'id_card' => '110101199003078888',
];
Mask::array($user, [
'name' => 'name',
'phone' => 'phone',
'id_card' => 'idCard',
]);如果全站想统一掩码字符(比如换成 #),可以用 Config 类设置默认值,不用每个调用点重复传参。优先级是:方法级 options['char'] > Config::get('char') > 默认 *,Config::reset() 清空配置。
还不够用时,实现 StrategyInterface 注册一个自定义策略,注册后同样能出现在 Mask::array() 的映射里。
日志脱敏是另一个典型场景——一句话里可能混着电话、身份证、邮箱。Mask::auto() 对长文本做智能识别,自动识别手机号、身份证、邮箱、车牌、银行卡、USCC、护照、港澳台证件等十类内容,还能识别 JSON 字符串并只脱敏其中的值:
$text = '我的电话是 13812345678,身份证是 110101199003078888,邮箱 foo@bar.com';
Mask::auto($text);
// "我的电话是 138****5678,身份证是 110101********8888,邮箱 f**@bar.com"
// 只脱敏指定类型,其余保留原样
Mask::auto($text, ['types' => ['phone', 'idCard']]);日志方面,库提供两个开箱即用的方案:MaskProcessor::process() 静态处理器,可以直接挂到任意日志框架的处理链上;MaskSensitiveLogger 则包一层实现了 PSR-3 的 LoggerInterface,业务代码完全不用改,写入前会自动脱敏消息文本。
Laravel 的集成是零配置的:服务提供者通过 extra.laravel.providers 自动注册,可以在代码里 Mask::phone($user->phone) 直接调用,也可以用 MaskedField 做成验证规则,让用户提交的数据必须已经脱敏过:
use MaskCn\Laravel\Rules\MaskedField;
$request->validate([
'phone_masked' => ['string', new MaskedField('phone')],
]);坦白说这个库还不算重存储级的方案——它解决的是"输出打码"这一层,不涉及加密存储或去标识化存储这类更深的数据保护。如果你的诉求是「让日志、API 返回、报表里的敏感字段不再明文跑」,它确实能省下不少自己维护正则规则的时间。
代码在 GitHub 上,MIT 协议,配套 PHPUnit 测试和 PHPStan 静态分析,大家在部署时遇到环境问题,可以到本站 /tool 和 /download 板块找相关的运行环境工具辅助调试。
相关阅读
扫描二维码获取文章详情
更多精彩内容尽在:WWW.ZNGG.NET
