必须停用直接记录$request->param()或$_post的写法,因其会导致密码、token等敏感信息明文写入日志;需统一使用脱敏函数masksensitivedata()递归处理并替换敏感字段为[redacted],且审计与调试日志须物理隔离、路径权限严格管控。

立刻停用直接记录 $request->param() 或 $_POST 的写法——这不是配置问题,是代码层硬伤,不改就等于把密码、token、身份证号实时广播给所有能碰日志的人。
ThinkPHP 里 Log::write() 记录参数前必须脱敏
框架不会帮你过滤,Log::write($request->param(), 'info') 这行代码运行一次,日志里就出现一次明文 "password": "123456"。别指望中间件或配置项自动生效。
- 所有调用
Log::write()的地方,替换成先过脱敏函数再写:比如Log::record(maskSensitiveData($request->param()), 'info') -
maskSensitiveData()要递归处理嵌套数组,键名大小写不敏感(pwd、PASSWORD、passWord都得匹配),统一替换为[REDACTED] - 别用
unset($data['password'])临时补救:遇到['user' => ['credentials' => ['pwd' => 'xxx']]这种结构就漏掉 - 支付回调、第三方授权、用户资料提交这些场景,
access_token、refresh_token、id_card、bank_card必须进脱敏白名单
审计日志和调试日志必须物理隔离
用同一个 Logger 实例混写 audit 和 debug 日志,等于把操作痕迹和堆栈信息塞进同一张嘴,脱敏策略根本没法统一。
- 创建独立 channel:
$auditLogger = new Logger('audit'),绑定专用StreamHandler到/var/log/myapp/audit.log - 禁用
LineFormatter中的%context%,它会把数组转成Array字符串,丢失字段细节;改用自定义 formatter 提取并清洗特定字段 - 高风险动作如
user_password_change、data_delete、config_update只记必要字段:user_id、action、target_id、status、timestamp,不拼接原始请求体 - 绝对不要在审计日志里写
echo、var_dump或页面渲染结果,那是调试日志的事
日志路径和权限比内容脱敏更基础
就算你把每条日志都脱敏了,如果日志文件能被 HTTP 直接下载,攻击者照样拿到时间戳、IP、操作类型、失败原因——足够支撑暴力破解或社工钓鱼。
- 确认
runtime/log/不在 Web 根目录下:Nginx 的root指向/var/www/myapp/public/,就把runtime放到/var/www/myapp/runtime/(与public同级) - 检查
log.path配置是否含../或相对路径,生产环境只允许绝对路径,且启动时用is_writable()主动校验 - 日志目录权限设为
640,属主是 PHP-FPM 运行用户(如www-data),组可读,其他无权限 - Web 服务器配置必须显式拒绝访问
.log、.env、.bak等扩展名,Nginx 用location ~ \.(log|env|bak)$ { deny all; },别依赖.htaccess
真正麻烦的不是写脱敏函数,而是那些没走统一日志入口的散点调用——比如某个异常捕获块里直接 error_log(print_r($_REQUEST, true)),这种代码藏在角落,只有出事时才浮出来。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











