PHP 项目安全漏洞识别与定位方法
整体思路:分层排查 + 工具辅助 + 人工审计
代码层 → 配置层 → 依赖层 → 运行时 → 人工验证
常见漏洞类型与识别定位方法
SQL 注入
识别特征

// 危险模式:变量直接拼接 $sql = "SELECT * FROM users WHERE id = " . $_GET['id']; $sql = "SELECT * FROM users WHERE name = '$_POST[name]'"; mysqli_query($conn, "DELETE FROM t WHERE id=$id");
定位方法
# 搜索拼接模式 grep -rn --include="*.php" -E "\$_(GET|POST|REQUEST|COOKIE).*?(SELECT|INSERT|UPDATE|DELETE)" . # 搜索危险的 SQL 构造 grep -rn --include="*.php" -E "(mysql_query|mysqli_query|->query)\s*\(.*\." . # 查找未使用预处理 grep -rn --include="*.php" "mysql_query\|mysqli_query" . | grep -v "prepare"
修复方向:PDO/MySQLi 预处理、参数绑定、ORM
XSS 跨站脚本
识别特征
echo $_GET['name']; // 反射型 echo $row['content']; // 存储型(未过滤) <?= $userInput ?> // 短标签输出
定位方法
# 查找直接输出用户输入 grep -rn --include="*.php" -E "echo\s+\\\$_(GET|POST|REQUEST)" . # 查找未转义的模板变量 grep -rn --include="*.php" -E "<\?=\s*\\\$" . # 检查 htmlspecialchars 使用 grep -rn --include="*.php" "echo" . | grep -v "htmlspecialchars\|htmlentities"
修复方向:输出时 htmlspecialchars($v, ENT_QUOTES, 'UTF-8')、CSP 头
文件包含 / 任意文件读取
识别特征
include $_GET['page'] . '.php'; require $_POST['file']; readfile($_GET['path']); file_get_contents($input);
定位方法
grep -rn --include="*.php" -E "(include|require|include_once|require_once)\s*\(?\s*\\\$" .
grep -rn --include="*.php" -E "(readfile|file_get_contents|fopen|file|show_source|highlight_file)\s*\(" .
排查要点:是否白名单、是否 realpath() 校验、是否限制目录
文件上传漏洞
定位点
move_uploaded_file($_FILES['file']['tmp_name'], $target);
检查清单
- [ ] 是否校验
$_FILES['file']['type'](不可靠) - [ ] 是否校验扩展名白名单
- [ ] 是否校验 MIME + 文件头(
finfo_file) - [ ] 上传目录是否可执行 PHP(nginx/apache 配置)
- [ ] 文件名是否被重命名(防解析漏洞、防路径穿越)
grep -rn --include="*.php" "move_uploaded_file\|\$_FILES" .
命令执行 / 代码执行
高危函数清单
grep -rn --include="*.php" -E "\b(system|exec|shell_exec|passthru|popen|proc_open|pcntl_exec)\s*\(" .
grep -rn --include="*.php" -E "\b(eval|assert|create_function|preg_replace.*\/e)\s*\(" .
grep -rn --include="*.php" -E "(call_user_func|call_user_func_array|array_map|usort).*\\\$" .
审计重点:参数是否来自用户输入、是否 escapeshellarg() 转义
反序列化漏洞
grep -rn --include="*.php" -E "\b(unserialize|igbinary_unserialize)\s*\(" .
配合检查:是否存在 __destruct / __wakeup / __toString 可利用的魔术方法链(POP chain)
grep -rn --include="*.php" "__destruct\|__wakeup\|__toString\|__call" .
修复:用 json_decode 替代,或使用 allowed_classes 白名单
SSRF 服务端请求伪造
grep -rn --include="*.php" -E "(curl_exec|file_get_contents|fsockopen|stream_socket_client)\s*\(.*http" .
审计要点:URL 是否可控、是否做协议/域名/IP 白名单、是否禁内网段(0.0.1、254.169.254、0.0.0/8 等)
越权(水平/垂直)
人工审计为主,重点:
- 所有敏感操作是否有权限校验(
is_admin、owner_id == current_user) - 是否通过隐藏参数控制权限(
?user_id=1002) - 是否使用 RBAC/中间件统一鉴权
# 查找可疑直接凭参数操作 grep -rn --include="*.php" -E "\\\$_(GET|POST)\['(user_)?id'\]" . | head -50
弱口令 / 敏感信息泄露
# 硬编码凭证
grep -rn --include="*.php" -Ei "(password|passwd|pwd|secret|api_key|access_key)\s*=\s*['\"]" .
# 调试开关
grep -rn --include="*.php" -E "display_errors|error_reporting\s*\(\s*E_ALL" .
# 敏感函数
grep -rn --include="*.php" -E "\b(phpinfo|var_dump|print_r|debug_zval_dump)\s*\(" .
会话与 CSRF
- 检查
session_start()后是否有session_regenerate_id(true)(登录后) - CSRF:表单是否有 token
- Cookie 是否
HttpOnly+Secure+SameSite
grep -rn --include="*.php" "session_regenerate_id\|csrf" .
工具链
| 工具 | 用途 |
|---|---|
| PHPStan / Psalm | 静态分析,类型污点跟踪 |
| phpcs-security-audit | PHP_CodeSniffer 安全规则 |
| Semgrep | 自定义规则做污点分析 |
| SonarQube | 综合静态扫描 |
| RIPS | 专业 PHP 污点分析(商业/开源版) |
| composer audit | 依赖漏洞(CVE) |
| OWASP Dependency-Check | 第三方组件风险 |
| Burp Suite / ZAP | 动态扫描(DAST) |
Semgrep 污点规则示例
rules:
- id: php-sqli
languages: [php]
message: SQL injection via $_GET
severity: ERROR
mode: taint
pattern-sources:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_REQUEST
pattern-sinks:
- pattern: mysqli_query(...)
- pattern: $X->query(...)
配置层检查清单
# php.ini 关键项 disable_functions = exec,passthru,shell_exec,system,proc_open,popen,eval,assert display_errors = Off expose_php = Off open_basedir = /var/www/yourapp allow_url_include = Off session.cookie_httponly = 1 session.cookie_secure = 1
Web 服务器
- 上传目录禁止 PHP 解析
- 关闭目录遍历
autoindex off - 合理设置
upload_max_filesize、post_max_size
依赖层检查
cd your-project composer audit # 已安装依赖的已知漏洞 composer outdated --direct # 版本过旧
重点关注:symfony/*、laravel/framework、guzzlehttp/guzzle、phpmailer/phpmailer、monolog/monolog
人工审计优先级(污点分析思路)
脏数据来源(Source)
$_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVER
php://input / getallheaders() / HTTP headers
危险汇聚点(Sink)
SQL 执行 / 命令执行 / 文件操作 / 模板输出 / 反序列化
HTTP 请求 / header() / setcookie()
审计路径:从 Sink 反查参数来源 → 判断路径上是否有过滤/白名单
落地建议流程
- 快速扫描:
composer audit+phpstan+semgrep全量跑一遍 - 定位高危:优先排查
eval/system、反序列化、文件上传/包含、SQL 拼接 - 人工审计:以"用户输入 → Sink"为线索走查
- 动态验证:本地起环境,用 Burp/ZAP 复现
- 修复回归:修复后加回归测试 + CI 门禁(semgrep/phpstan 阻断)
需要针对某一类漏洞(如反序列化 POP 链、或 Laravel/ThinkPHP 特定框架的审计套路)深入的话,可以继续问。