thinkphp6.x中需对password、mobile等敏感字段脱敏:一、自定义日志处理器全局脱敏;二、log::record()接口级脱敏;三、模型访问器实现展示层动态脱敏;四、模型修改器结合aes加密实现存储层双向脱敏;五、查询阶段字段裁剪实现权限级脱敏。

如果您在ThinkPHP6.x项目中记录用户请求参数或渲染用户数据时,日志文件或前端页面直接暴露password、mobile、id_card等原始值,则存在严重隐私泄露风险。Log::write()不识别模型$hidden规则,也不调用访问器,更不会自动递归扫描嵌套数组中的敏感键——这意味着明文写入即违规。以下是多种可行的脱敏处理方法:
一、自定义日志处理器实现全局脱敏
通过继承think\log\driver\File并重写save()方法,在日志落盘前对全部$data执行统一脱敏,可覆盖HTTP请求、命令行、定时任务及异常捕获等所有日志来源,是最彻底的防护方式。
1、创建app/common/log/SecureFile.php文件,继承think\log\driver\File类。
2、在save()方法内调用递归脱敏函数maskSensitiveData($data),使用array_walk_recursive()遍历所有值,匹配password、pwd、token、id_card、bank_card、mobile、phone等关键词(大小写不敏感)。
3、将匹配字段值替换为***,禁止使用空字符串以防被误判为字段缺失。
4、在app/common.php中注册该处理器:Log::setHandler(new \app\common\log\SecureFile());
二、使用Log::record()进行接口级轻量脱敏
当仅需对特定控制器或接口的日志做精准控制时,Log::record()绕过默认日志通道,支持预处理后写入,避免全局修改带来的耦合与测试成本。
1、在控制器中获取原始参数:$params = $request->param();
2、调用脱敏函数处理:$params = maskSensitiveData($params);
3、手动记录日志:Log::record('登录请求参数: ' . json_encode($params, JSON_UNESCAPED_UNICODE), 'info');
4、确保JSON编码启用JSON_UNESCAPED_UNICODE,防止中文乱码;不可直接写Log::write($request->param(), 'info')。
三、模型访问器(Accessor)实现展示层动态脱敏
在模板渲染、API输出或toArray()调用过程中,通过getAttr访问器对字段值实时变形,使手机号、身份证号等按规则掩码,且对所有序列化出口生效,无需重复调用逻辑。
1、在User模型中定义getPhoneAttr方法:public function getPhoneAttr($value) { return substr($value, 0, 3) . '****' . substr($value, -4); }
2、定义getIdCardAttr方法:public function getIdCardAttr($value) { return substr($value, 0, 6) . '******' . substr($value, -4); }
3、确保访问器命名严格符合get + 字段名首字母大写 + Attr格式;直接调用$model->toArray()即可触发脱敏,无需额外处理。
四、模型修改器(Mutator)结合加密实现存储层双向脱敏
对数据库中真实存储的敏感字段(如id_card、bank_card),在写入时加密、读取时解密,实现源头保护。该方式要求加解密逻辑透明嵌入模型生命周期,不影响业务代码调用习惯。
1、在User模型中定义setIdCardAttr方法:使用openssl_encrypt($value, 'AES-256-CBC', env('AES_KEY'), 0, env('AES_IV'))加密后存入。
2、定义getIdCardAttr方法:使用openssl_decrypt($value, 'AES-256-CBC', env('AES_KEY'), 0, env('AES_IV'))解密后返回。
3、确保.env中配置AES_KEY和AES_IV为32字节与16字节随机字符串;密钥严禁硬编码,必须通过env()动态加载。
五、查询阶段字段裁剪实现权限级脱敏
针对不同角色用户(如管理员与普通用户),在SQL层面剔除无权查看的敏感字段,从源头杜绝内存残留与传输泄露,适用于强合规场景。
1、在控制器中根据当前用户权限构建白名单字段:$fields = $user->can('view_id_card') ? ['name', 'phone', 'id_card'] : ['name', 'phone'];
2、调用查询时显式指定字段:UserModel::field($fields)->find($id);
3、避免使用select * 或 findAll();field()裁剪发生在SQL层,未查询的数据不会进入PHP内存。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











