$npx -y skills add 0xShe/PHP-Code-Audit-Skill --skill php-logging-auditPHP Web 安全日志与监控审计工具。识别安全事件缺失、敏感信息写入日志、日志注入/伪造风险,以及日志与告警链路缺陷,并输出可利用性分级、可观测 PoC 与修复建议(禁止省略)。
| 1 | # PHP 安全日志与监控审计(php-logging-audit) |
| 2 | |
| 3 | 分析 PHP 项目在“安全可观测性”方面的代码与配置缺陷。核心目标不是泛化“要不要打日志”,而是识别可落地的问题:攻击者可否通过输入污染日志、系统是否把敏感信息写入日志、关键安全事件是否缺失、以及告警/审计链是否被破坏。 |
| 4 | |
| 5 | ## 分级与编号 |
| 6 | - 详见:`shared/SEVERITY_RATING.md` |
| 7 | - 漏洞编号:`{C/H/M/L}-LOG-{序号}` |
| 8 | |
| 9 | ## 覆盖范围(必做:必须逐项检查并输出证据) |
| 10 | 本 skill 必须覆盖下列四类缺陷,并为每一类给出“证据点 + 影响 + 可利用性分析 + 修复建议”: |
| 11 | |
| 12 | ### 1) 安全事件审计缺失(Audit Gap) |
| 13 | 识别缺失的安全事件日志点,例如(以项目实际入口为准): |
| 14 | - 登录失败/成功与账号枚举相关失败原因记录(避免泄露敏感细节) |
| 15 | - 权限拒绝/越权尝试(拒绝原因可抽象化记录) |
| 16 | - 密码重置、邮箱变更、管理员角色变更、2FA 变更等关键账户事件 |
| 17 | - 敏感操作(导出数据、下载密钥、删除/修改权限、批量导入) |
| 18 | - CSRF 校验失败、会话失效、注销成功等安全相关状态变化 |
| 19 | |
| 20 | 要求输出: |
| 21 | - “应当记录哪些事件”(按你在源码里识别到的功能点映射) |
| 22 | - “实际写日志的位置是否存在/是否被绕过”(给出证据) |
| 23 | - 缺失导致的真实后果推演(例如审计不可用、事后无法追溯、告警无法触发) |
| 24 | |
| 25 | ### 2) 敏感信息写入日志(Sensitive Data in Logs) |
| 26 | 识别任何可能把敏感数据写入日志的路径: |
| 27 | - `Authorization` 头、Cookie、`session_id`、JWT 原文 |
| 28 | - 密码/验证码/恢复 token/一次性口令 |
| 29 | - 用户隐私数据在失败栈或 debug 输出中被原样打印 |
| 30 | - 将请求体/参数(尤其是包含密钥字段)直接拼进日志消息 |
| 31 | |
| 32 | 要求输出: |
| 33 | - 敏感信息来源(来自哪些变量/请求字段) |
| 34 | - 写日志的调用点与 message 构造方式 |
| 35 | - 日志的落地介质(文件路径/系统日志通道/日志库) |
| 36 | |
| 37 | ### 3) 日志注入与伪造(Log Injection / Forging) |
| 38 | 识别“用户可控字符串进入日志消息但未做控制字符净化”的问题: |
| 39 | - `\r` / `\n` 进入日志消息导致日志行拆分 |
| 40 | - 可能造成伪造时间戳、级别、请求路径等“假条目” |
| 41 | - 若日志格式使用 JSON 或键值对,检查是否存在转义缺陷造成结构被破坏 |
| 42 | |
| 43 | 要求输出: |
| 44 | - 哪些字段可控(GET/POST/Headers/Body/Cookie) |
| 45 | - 注入进入点(日志 message 拼接点) |
| 46 | - 是否存在过滤/归一化(尤其是换行与控制字符处理) |
| 47 | |
| 48 | ### 4) 日志与告警链路缺陷(Monitoring & Alert Gaps) |
| 49 | 识别监控链路缺口(以代码/配置能证明的为准): |
| 50 | - 关键安全事件只记录 debug/info,未进入可告警通道 |
| 51 | - 日志级别与告警规则缺失(例如 production 关闭了安全告警 channel) |
| 52 | - 错误处理吞掉异常导致关键事件没有被写入 |
| 53 | |
| 54 | 要求输出: |
| 55 | - 你在源码里定位到的日志级别/通道/handler 配置证据 |
| 56 | - 这些证据如何导致告警无法触发或取不到关键证据 |
| 57 | |
| 58 | ## 识别逻辑(必做) |
| 59 | ### 日志写入 Sink(代码) |
| 60 | 必须识别以下日志相关 API(按项目实际替换): |
| 61 | - PHP:`error_log({value})` |
| 62 | - 系统:`syslog({value})` |
| 63 | - PSR-3/框架:`->info({value})` `->warning({value})` `->error({value})` `->critical({value})` `->debug({value})` |
| 64 | - Laravel/Symfony/自定义封装:`Log::{value}({value})`、`logger->{value}` 或项目自己的 logger wrapper |
| 65 | - Monolog:`Monolog\Logger` 及 handler/channel 相关配置 |
| 66 | |
| 67 | ### 消息构造与可控性(数据流) |
| 68 | 对每个日志写入点,必须追踪至少一条链: |
| 69 | - Source(请求字段/用户输入/敏感 token 来源) |
| 70 | -> 中间变量处理(拼接、格式化、json_encode、模板字符串) |
| 71 | -> Sink(最终写入日志的 message 参数) |
| 72 | |
| 73 | 并输出: |
| 74 | - 可控字段名(或 JSON 路径) |
| 75 | - 进入日志前是否有净化(换行/控制字符/敏感字段脱敏) |
| 76 | - 是否有条件分支导致“只有某些分支才写日志”(写日志绕过) |
| 77 | |
| 78 | ## PoC(必做:可观测验证框架) |
| 79 | 由于“日志缺陷”往往需要观察日志输出,你必须给出可观测 PoC 框架(可执行或可手动验证),至少包括以下两类其一: |
| 80 | - 使用真实路由触发日志写入,并说明预期日志中出现的字段/格式变化 |
| 81 | - 使用构造 payload 注入换行/控制字符,说明预期日志会被拆行或出现伪造片段 |
| 82 | |
| 83 | PoC 输出要求: |
| 84 | - 必须包含真实路由(优先从 `routes_{timestamp}.md` 替换;独立落盘见 `route_mapping/routes_{timestamp}.md`,约定见 `shared/IO_PATH_CONVENTION.md`) |
| 85 | - 必须写出用于触发的请求头与 body/参数(至少包含一个会进入日志消息的可控字段) |
| 86 | - 必须说明你要在日志文件/控制台/系统日志中观察什么(例如某字段是否原样出现) |
| 87 | |
| 88 | ## 报告输出 |
| 89 | 输出到: |
| 90 | ``` |
| 91 | {output_path}/vuln_audit/logging_{timestamp}.md |
| 92 | ``` |
| 93 | |
| 94 | ## 漏洞条目模板(强制) |
| 95 | 每条漏洞必须遵循以下结构(不得省略): |
| 96 | ```markdown |
| 97 | ### [{等级前缀}-LOG-{序号}] {风险标题} |
| 98 | |
| 99 | | 项目 | 信息 | |
| 100 | |------|------| |
| 101 | | 严重等级 | {🔴/🟠/🟡/🔵} (CVSS {score}) | |
| 102 | | 可达性 (R) | {0-3} - {理由} | |
| 103 | | 影响范围 (I) | {0-3} - {理由} | |
| 104 | | 利用复杂度 (C) | {0-3} - {理由} | |
| 105 | | 可利用性 | ✅ 已确认 / ⚠️ 待验证 / ❌ 不可利用 / 🔍 环境依赖 | |
| 106 | | 位置 | {file}:{line} ({Function/Class}) | |
| 107 | |
| 108 | #### 漏洞类型与证据点 |
| 109 | - 缺陷类别:{Audit Gap / Sensitive Data in Logs / Log Injection / Monitoring Gap} |
| 110 | - 证据点 A(日志写入调用点):{file}:{line} 说明 |
| 111 | - 证据点 B(消息构造/可控字段进入点):{file}:{line} 说明 |
| 112 | - 证据点 C(净化/脱敏是否存在):{file}:{line} 说明(如无则写“未发现脱敏/未发现过滤”并给出搜索结论) |
| 113 | |
| 114 | #### 数据流链(Source -> Transform -> Sink) |
| 115 | (逐行写出:请求输入如何被读取/解析/格式化 -> 是否进入日志 message -> 最终写入日志) |
| 116 | |
| 117 | #### 可利用前置条件 |
| 118 | - 鉴权要求:{无需/需登录/需特定权限} |
| 119 | - 输入可控性:{完全可控/条件可控/不可控} |
| 120 | - 触发条件:{分支/错误路径/异常路径/日志级别条件} |
| 121 | - 可观测性:{日志落地位置是否可读/是否在生产可见/是否需要运维权限} |
| 122 | |
| 123 | #### 验证 PoC(强制,可观测) |
| 124 | ```http |
| 125 | {HTTP Method} {真实路由与完整参数} HTTP/1.1 |
| 126 | Host: {host} |
| 127 | {必要 Header/Session/JWT/Cookie} |
| 128 | |
| 129 | {Payload} |
| 130 | ``` |
| 131 | (PoC 之后你需要观察的日志现象:{明确写出预期日志内容/格式变化/是否出现换行拆分}) |
| 132 | |
| 133 | #### 建议修复 |
| 134 | - 安全写法要点:敏感字段脱敏、控制字符净化、structured logging、日志级别与告警映射 |
| 135 | - 代码搜索语句:使用 `rg` 定位所有日志写入与 message 构造相关代码块 |
| 136 | ``` |
| 137 | |
| 138 | ## 输出完整性检查(强制) |
| 139 | - [ ] 每条漏洞都包含:编号、等级、位置、数据流链、可利用前置条件、可观测 PoC、修复建议 |
| 140 | - [ ] 日志注入类问题必须明确使用控制字符 payload(至少包含换行注入场景) |
| 141 | - [ ] 敏感信息类问题必须明确指出敏感字段来源与写入位置 |
| 142 | - [ ] 审计缺失类问题必须明确指出“应该记录但未记录”的对照逻辑(基于源码事件点推断,而不是泛泛而谈) |
| 143 | ``` |