PHP 项目防守漏洞识别与定位指南
防守方(甲方/蓝队/安全左移)识别 PHP 项目漏洞,需要从代码审计、运行时监控、依赖管理、配置检查几个维度系统化进行,下面给出一套可落地的方法论。

先建立"漏洞定位思维模型"
定位漏洞本质是回答三个问题:
- 入口在哪?(用户可控数据从哪进来)
- 数据怎么流?(污点传播路径)
- 危险函数在哪?(sink 点是否被触发)
即经典的 Source → Propagator → Sink 污点分析模型。
静态代码审计(SAST)—— 主力手段
按高危 Sink 反向搜索
用 IDE / grep / ripgrep 全局搜索危险函数,这是最快定位方式:
命令执行类
rg -n "system|exec|shell_exec|passthru|popen|proc_open|pcntl_exec|`" --type php
代码执行类
rg -n "eval|assert|create_function|preg_replace.*/e|call_user_func|call_user_func_array|array_map|usort" --type php
文件操作类
rg -n "include|require|include_once|require_once|file_get_contents|file_put_contents|fopen|unlink|copy|rename|move_uploaded_file" --type php
SQL 注入类
rg -n "mysql_query|mysqli_query|->query\(|->exec\(|->prepare\(" --type php
反序列化类
rg -n "unserialize|__wakeup|__destruct|__toString|__call" --type php
SSRF/请求类
rg -n "curl_exec|file_get_contents\(.*http|fsockopen|stream_context_create" --type php
追踪污点传播
找到 Sink 后往回追,看参数是否来自:
$_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVERphp://input- 数据库读出的内容(二次注入)
- 上传文件名、HTTP Header
定位要点:只要从入口到 Sink 之间没有白名单校验,基本可以判定为漏洞。
静态分析工具
| 工具 | 特点 |
|---|---|
| PHPStan / Psalm | 类型流分析,配合 taint 扩展 |
| psalm --taint-analysis | 官方支持污点分析,强烈推荐 |
| RIPS | 商业 PHP 静态审计,误报低 |
| SonarQube | 通用,支持 PHP |
| Semgrep | 自定义规则灵活 |
| Fortify / Checkmarx | 企业级 |
Psalm 污点分析示例:
psalm --taint-analysis --output-format=json ./src
它会直接给出 $_GET['id'] → mysqli_query() 的完整路径。
通读路由入口
PHP 项目定位漏洞的第一站:
- 框架项目:看
routes/、controller/、middleware/ - 原生项目:看
index.php入口、.htaccess/nginx.conf的 rewrite 规则 - 找出所有对外暴露的 URL,逐个反查代码
动态检测(DAST)—— 运行时验证
黑白盒扫描工具
- Burp Suite / AWVS / Xray / 杀器(Goby):主动扫描 URL
- sqlmap:SQL 注入定位
- nuclei:模板化 POC 扫描
运行时 Hook(关键手段)
在 PHP 层挂钩危险函数,直接捕获调用栈:
// 通过 auto_prepend_file 注入
$danger = ['system','exec','eval','unserialize','mysqli_query'];
foreach ($danger as $fn) {
// 需要 uopz / runkit 扩展实现函数替换
}
更实用的方式是 RASP(运行时应用自我保护):
- 开源:
openrasp(支持 PHP) - 商业:长亭、青藤、默安等 HIDS/RASP
RASP 会直接输出:
漏洞类型: 命令执行
调用栈: index.php:10 → UserController.php:55 → system()
输入源: $_GET['cmd'] = "whoami"
日志与埋点
error_log+ PHP-FPM access log- 在 PDO/mysqli 层包装记录完整 SQL
- Nginx
$request_body全量记录(注意脱敏)
依赖与配置层防护
Composer 依赖扫描
composer audit # 或 local-php-security-checker composer.lock
对接 https://packagist.org 漏洞库,定位 symfony/*、laravel/*、guzzle 等组件的已知 CVE。
配置检查清单
| 配置项 | 安全值 |
|---|---|
display_errors |
Off |
allow_url_include |
Off |
allow_url_fopen |
按需 |
disable_functions |
system,exec,passthru... |
open_basedir |
限定目录 |
session.cookie_httponly |
On |
session.cookie_secure |
On |
expose_php |
Off |
php -i | grep -E "disable_functions|open_basedir|allow_url"
常见 PHP 漏洞的定位套路
| 漏洞类型 | 快速定位方法 |
|---|---|
| SQL 注入 | 搜 $_GET/\$_POST 直接影响 query;检查是否用 prepare + 参数绑定 |
| XSS | 搜 echo/print 输出未 htmlspecialchars 的变量 |
| 文件包含 | 搜 include/require 的变量参数,检查是否含 / php:// |
| 文件上传 | 检查 move_uploaded_file 前的 MIME、后缀、内容校验 |
| 命令执行 | 搜 system/exec/shell_exec,看参数是否拼接 |
| 反序列化 | 搜 unserialize,追踪 __destruct/__wakeup 可达性(POP 链) |
| SSRF | 搜 curl_exec/file_get_contents,参数是否用户可控 |
| 越权 | 看 controller 是否有统一的鉴权中间件 |
| CSRF | 检查表单是否有 token 校验 |
落地流程(推荐)
梳理资产 → 路由入口清单 + 对外接口
2. 静态扫描 → Semgrep/Psalm + grep 危险函数
3. 人工审计 → 按 Source→Sink 追踪每个入口
4. 动态验证 → Burp + Xray + RASP 复现
5. 依赖审计 → composer audit
6. 配置加固 → php.ini + Nginx + disable_functions
7. 建立基线 → 把审计规则沉淀到 CI(GitLab CI / GitHub Actions)
CI 集成示例:
stages: [scan] semgrep: script: semgrep --config=p/php --error . psalm: script: psalm --taint-analysis composer-audit: script: composer audit
关键建议
- 不要只依赖工具:SAST 误报率高,最终靠人工确认数据流。
- 优先看入口:80% 的洞来自用户可控的
$_GET/$_POST直接进危险函数。 - 关注框架特性:Laravel/ThinkPHP 的
Request封装、DB::raw、whereRaw都是高危点。 - RASP 是防守方利器:能在运行时直接告诉你漏洞在哪一行。
- 建立函数禁用清单:即使代码有洞,
disable_functions也能兜底。
如果你想针对具体某个漏洞类型(如反序列化链、SQL 注入)或者某个框架(Laravel/ThinkPHP)展开,我可以给更细的定位步骤。