敏感字段需多层脱敏:模型$hidden仅影响API响应;日志需自定义tap处理器;API响应用中间件递归替换;Excel导出用WithMapping掩码;非序列化路径须按场景单独处理。

敏感字段在 Eloquent 模型里怎么自动隐藏
直接在模型里用 $hidden 或 $casts 控制输出,但要注意:这仅影响 toArray() 和 API JSON 响应,对 dd()、日志打印、数据库查询结果本身完全无效。
-
$hidden = ['password', 'api_token']最简单,但硬编码字段名,改表结构时容易漏同步 - 更稳妥的是用
$casts配合访问器:'password' => 'encrypted'(需自定义 cast 类)或直接在getPasswordAttribute里返回''或'[hidden]' - 如果字段要「读取时解密、输出时隐藏」,别在访问器里做解密逻辑——否则
where('password', ...)会失效;应拆成getDecryptedPasswordAttribute+ 单独的隐藏规则
Laravel 日志里密码为啥还在?怎么过滤
默认 Log::info($user) 会调用模型的 __toString() 或 dump 全部属性,$hidden 完全不生效。关键在日志通道的处理器。
- 在
config/logging.php的stack或具体 channel 下,加'tap' => [App\Logging\HideSensitiveData::class] - 自定义 tap 类里用
CollectsLogstrait,重写tap方法,对$context中的User、Request等对象递归 unset 敏感键(如password,token,card_number) - 别依赖
str_replace替换日志字符串——JSON 结构可能嵌套,且会误杀正常含 "pass" 的字段(比如passenger_count)
API 接口返回前统一脱敏,不用每个控制器写 if
用响应中间件最干净,比在 Resource 里判断更可控,也避免 Resource 被复用时漏脱敏。
- 运行
php artisan make:middleware SanitizeApiResponse,在handle里检查$response->headers->get('Content-Type')是否含json - 对
$response->getContent()解码后递归遍历数组,匹配字段名白名单(如['password', 'ssn', 'id_card']),值统一替换为'[REDACTED]'或空字符串 - 注意:不能直接
json_encode回去——要保留原始响应头(如状态码、缓存头),所以得用Response::make(..., $response->getStatusCode(), $response->headers)
数据库查询结果导出 Excel 时泄露手机号怎么办
导出不是走 API 响应流程,$hidden 和中间件都无效。必须在数据组装阶段处理。
- 别在
toArray()里硬删字段——Excel 导出常需要原始数据结构做样式或公式,删了反而难补 - 用 Laravel Excel 的
WithMapping关口:实现map方法,对每行数据做字段映射,例如return [$row->name, $this->maskPhone($row->phone), $row->email]; -
maskPhone建议写成工具函数,支持不同掩码格式(138****1234或138****),并加缓存或条件开关(比如开发环境不掩码,方便调试)
脱敏不是加个 $hidden 就完事。真正麻烦的是那些不走 Eloquent 序列化的路径:日志、队列任务 payload、缓存 dump、DB raw 查询结果、第三方 SDK 透传的数据——这些地方没统一拦截点,得按场景一个个补漏。


















