必须先脱敏再记录请求参数,使用递归遍历原始数组并统一替换敏感键名为[REDACTED];分页需将POST转JSON塞入page-parameter;存储应异步+归档,缓存key须与日志脱敏逻辑一致。

请求参数直接存日志会泄露敏感信息
直接 json_encode($request->param()) 后写入日志,等于把 password、token、id_card 全部明文落盘。线上环境被拖库后,这些日志文件就是第一波攻击入口。
必须先脱敏再记录,且脱敏逻辑要覆盖嵌套数组和大小写变体:
- 键名匹配需用
strtolower(),因为前端可能传PWD或Access-Token - 值统一替换为
[REDACTED],不用***——后者容易被误认为是有效数据片段 - 禁止在
json_encode()之后用正则替换字符串,JSON 结构会被破坏,字段定位失效
正确做法是递归遍历原始数组,边遍历边替换:
function maskSensitiveData($data) {
$sensitiveKeys = ['password', 'pwd', 'token', 'access_token', 'id_card', 'bank_card', 'mobile', 'phone', 'email', 'card_no'];
if (!is_array($data)) return $data;
foreach ($data as $k => $v) {
if (in_array(strtolower($k), $sensitiveKeys)) {
$data[$k] = '[REDACTED]';
} elseif (is_array($v)) {
$data[$k] = maskSensitiveData($v);
}
}
return $data;
}
$params = $request->param();
$masked = maskSensitiveData($params);
Log::record('请求参数: ' . json_encode($masked, JSON_UNESCAPED_UNICODE), 'info');
分页场景下 POST 参数怎么持久化到 URL
用户用表单提交了模糊搜索条件,点分页链接时参数全丢了——这是因为分页默认只拼 GET 参数,而 POST 数据没被带过去。
立即学习“PHP免费学习笔记(深入)”;
解决核心就一条:把原始 $_POST 数组转成 JSON 字符串,塞进 $page->parameter:
$page->parameter = 'filter=' . urlencode(json_encode($_POST))- 前端无需改任何 HTML 或 JS,分页链接自动带上完整查询条件
- 服务端收到
$_GET['filter']后用json_decode($_GET['filter'], true)还原即可 - 注意不要用
$map(数据库查询条件)去存,它含 SQL 字段名、二维结构,不可逆也不稳定
示例关键代码段:
if (!empty($_GET['filter'])) {
$filter = json_decode($_GET['filter'], true);
} elseif (!empty($_POST)) {
$filter = $_POST;
} else {
$filter = [];
}
$map = $this->getFilterMap($filter); // 转换为 where 条件
$count = $model->where($map)->count();
$page = new Page($count, config('paginate.list_rows'));
$page->parameter = 'filter=' . urlencode(json_encode($filter));
$show = $page->show();
想存数据库而不是日志?别手写 INSERT
把每次请求参数硬 INSERT 到数据库,很快会撑爆磁盘。更现实的做法是异步写入 + 定期归档。
如果真要落地存储,优先走队列或缓存中转:
- 用
cache('request_log_' . time(), $masked, 60)先暂存,由定时任务批量入库 - 表结构必须包含
ip、path、method、create_time字段,否则无法关联分析 - 禁止对
param字段建索引——内容太长,索引体积爆炸 - 字段类型用
TEXT而非JSON,避免 MySQL 版本兼容问题(尤其 TP 部署在低版本环境时)
缓存层要不要存请求参数?看场景
请求缓存(request_cache)本身不保存参数,只根据 URL 或自定义标识命中缓存响应。但你可以主动把参数注入缓存 key:
- 路由定义里用
['cache' => ['search/:keyword/:page', 3600]],其中:keyword是实际参数值 - 行为钩子中调用
Request::instance()->cache('api/user/' . input('id'), 600) - 注意:缓存 key 中不要拼接未过滤的原始参数,防止 key 注入或超长溢出
- 若参数含敏感字段,必须先脱敏再参与 key 构造,否则缓存系统本身就成了泄露通道
真正容易被忽略的是:缓存 key 的生成逻辑和日志脱敏逻辑必须保持一致,否则同一请求在日志里是 [REDACTED],在缓存 key 里却是明文 token=abc123——这等于开了个暗门。



















