本文目录导读:

为PHP项目做代码审计是一项系统性的工作,需要结合自动化工具与手动审查,以下是针对PHP项目安全审计的完整方法论,涵盖常见漏洞、检测技巧及最佳实践:
审计前的准备工作
-
环境搭建:
- 本地化部署: 将PHP项目及其依赖(如MySQL、Redis、Composer包)完整运行在本地或隔离的容器中。
- 开启错误报告: 在
php.ini中设置display_errors = On并设置error_reporting = E_ALL,便于发现潜在错误。 - 启用Xdebug: 用于单步调试和变量跟踪。
-
工具准备:
- 静态分析工具:
- RIPS(已停止维护但经典): 适合PHP历史项目。
- Phan / PHPStan / Psalm: 代码质量检查,也能发现部分类型相关漏洞。
- SonarQube + PHP插件: 全面代码质量和安全问题扫描。
- Semgrep: 规则驱动的轻量级静态分析。
- 依赖检查工具:
composer audit(用于发现已知漏洞的第三方包)、Snyk、OWASP Dependency-Check。 - IDE/编辑器集成: PhpStorm(内置代码检查和PSR规范)、VS Code + PHP Intelephense。
- 静态分析工具:
-
获取代码结构:
- 理解项目使用的框架(Laravel、ThinkPHP、Symfony、Yii等)或原生开发模式。
- 识别入口文件(
index.php、router.php、public/)。 - 查看依赖管理文件(
composer.json、package.json、composer.lock)。
核心审计清单:以漏洞类型为导向
SQL注入
- 检查点:
- 原生SQL写法(最危险):
mysqli_query("SELECT * FROM users WHERE id = " . $_GET['id'])。- 修复: 强制使用参数化查询(PDO的
prepare/execute或 MySQLi的bind_param)。
- 修复: 强制使用参数化查询(PDO的
- ORM框架的误用:
- Laravel的
DB::select("select * from users where id = ?", [$id])(安全)。 - 但如果是
DB::select("select * from users where id = $id")(不安全)。 - ThinkPHP的
where('id', $id)(一般安全) vswhere('id = ' . $id)(不安全)。
- Laravel的
- 存储过程调用: 检查是否对传入参数进行了转义。
- 盲注检测: 观察
time、sleep函数是否出现在SQL查询逻辑中。
- 原生SQL写法(最危险):
跨站脚本攻击(XSS)
- 检查点:
- 输出点:
echo $_GET['name']、print_r、<?=$var?>且未使用htmlspecialchars()、htmlentities()或模板引擎转义。 - 上下文陷阱:
- HTML上下文: 使用
htmlspecialchars($var, ENT_QUOTES, 'UTF-8')。 - HTML属性上下文:
<a href="<?=$url?>">需额外处理单/双引号转义。 - JavaScript上下文:
<script>var x = '<?=$var?>';</script>极易被注入,应使用JSON编码(json_encode)。 - CSS/URL上下文: 谨慎拼接用户输入。
- HTML上下文: 使用
- 存储型XSS: 用户输入写入数据库后未转义直接显示(如评论、用户名)。
- 富文本XSS: 使用HTMLPurifier等白名单过滤器处理。
- 输出点:
文件包含漏洞(LFI/RFI)
- 检查点:
include($_GET['page'])、require_once "files/" . $user_input。- 使用
file_get_contents()、fopen()、include_once直接拼接用户路径。 - 危险函数:
include、include_once、require、require_once、file_get_contents、fopen、fread。 - 检测伪协议: 代码是否允许
php://input、php://filter、zip://、phar://等。
- 修复: 限制文件路径为白名单,或使用
realpath()规范化路径。
文件上传漏洞
- 检查点:
- 文件类型检测: 仅校验
$_FILES['file']['type'](客户端MIME,可篡改)而不对服务端getimagesize()或exif_imagetype()验证。 - 黑名单绕过: 仅拦截
.php、.php5、.phtml,但允许.pht、.php7、.shtml、.phar、.inc等。 - 上传路径: 是否允许覆盖系统文件(如
../config.php)。 - 二次渲染: 图片文件尾部隐藏PHP代码(可绕过
getimagesize())。 - WebShell检测: 检查上传后文件是否可访问。
- 文件类型检测: 仅校验
- 修复: 使用白名单扩展名、重命名文件(UUID)、将文件放在web根目录外或限制执行权限。
命令注入(OS Command Injection)
- 检查点:
exec("ping -c 4 " . $_GET['ip'])(直接拼接双引号、反引号、、、)。system()、passthru()、shell_exec()、proc_open()、popen()等函数。- PHP内部命令执行:
eval()、assert()、preg_replace('/e/')(已废弃)、create_function()、array_map()带用户回调。
- 修复: 对输入进行严格白名单校验,或使用
escapeshellarg()/escapeshellcmd()。
逻辑漏洞 & 访问控制
- 检查点:
- 权限缺失: 后台/管理页面是否允许未登录或低权限用户访问(如
admin.php未做if (isAdmin())检查)。 - IDOR(不安全的直接对象引用):
delete.php?id=123,用户可遍历ID删除他人数据(检查$user->id == $owner_id)。 - CRSF(跨站请求伪造): 关键操作(改密、转账)是否没有
Token或Referer验证。 - 会话管理:
session_id是否可预测,session是否在https下设置了Secure和HttpOnly标志。 - 密码安全: 使用
md5()或sha1()而不加salt。
- 权限缺失: 后台/管理页面是否允许未登录或低权限用户访问(如
不安全的配置
- php.ini:
display_errors = On(生产环境应关闭)。allow_url_include = On(应关闭,防止RFI)。magic_quotes_gpc = On(已废弃,不应依赖)。session.use_strict_mode = 0(应开启)。
- 框架/中间件:
debug模式在生产环境开启。.env文件未添加到.gitignore且暴露了数据库密码。log文件(如storage/logs/laravel.log)可被直接访问。
实战审计步骤
- 全局依赖审计:
- 运行
composer audit检查composer.lock中的已知漏洞。
- 运行
- 入口文件与路由审计:
- 从
index.php或框架路由文件(如routes/web.php)开始,追踪用户数据的流向。
- 从
- 敏感函数搜索:
- 使用grep或代码分析工具搜索以下危险函数(按优先级):
eval、preg_replace(带/e)、assert、create_function(命令执行)include、require、file_get_contents(文件包含)shell_exec、exec、system、passthru、popen(命令执行)mysql_query、mysqli_query、PDO::query(无预处理)unserialize(反序列化漏洞)header(未过滤回车换行注入)
- 使用grep或代码分析工具搜索以下危险函数(按优先级):
- 数据流追踪:
- 输入: 所有
$_GET、$_POST、$_COOKIE、$_FILES、$_SERVER、$_REQUEST。 - 传递: 这些变量如何经过函数处理?是否被
json_decode、base64_decode、unserialize处理? - 输出: 执行
echo、print、<?=的位置。
- 输入: 所有
- 自动化工具扫描:
- 使用 Semgrep(强烈推荐) 运行规则(如
p/php-security)。 - 用 RIPS 或 SonarQube 生成报告,然后手动验证真/假漏洞。
- 使用 Semgrep(强烈推荐) 运行规则(如
- 手动深入审查:
- 针对高风险的SQL/XSS/文件上传区域,逐行阅读逻辑。
- 检查框架自定义过滤器(如Laravel中定制的
Request规则)是否覆盖了所有场景。
最实用的工具组合
| 阶段 | 工具 | 用途 |
|---|---|---|
| 静态分析 | Semgrep | 自定义规则,精准发现模式匹配漏洞 |
| 静态分析 | PhpStorm + Intelephense | IDE实时提示、类型推断、超简化数据流追踪 |
| 已知漏洞 | composer audit | 快速检查第三方库漏洞 |
| Web扫描 | Burp Suite | 手动拦截修改请求、重放测试、SQL/XSS盲注测试 |
| 环境检测 | PHPStan (level 5+) | 检测未定义变量、类型混淆(可能导致逻辑漏洞) |
常见误区
- 依赖框架过滤器: 不要认为Laravel的
csrf或validation完全阻止了XSS,框架只过滤用户输入但不会自动对输出做HTML转义。 - 过度信任
addslashes(): 在GBK编码下,addslashes()绕过经典案例(宽字节注入)。 - 忽视
unserialize(): 反序列化漏洞在PHP中非常普遍,检查所有使用unserialize的地方。 - 忽略文件上传的二次利用: 上传一张图片后,文件名、路径信息可能被用于其他漏洞(如包含、移动、重命名)。
输出审计报告
一份标准的PHP审计报告应包含:
- 漏洞摘要: 按严重程度(Critical/High/Medium/Low)分类。
- 详细描述: 问题位置、代码片段、测试步骤(如复制Burp请求)。
- 危害分析: 此漏洞可能造成的影响(数据泄露、服务器沦陷等)。
- 修复建议: 具体代码修改方案(如替换函数名、增加过滤条件)。
对PHP代码审计,静态分析帮你快速定位可疑的“危险函数”,动态测试(抓包+手动尝试) 验证漏洞是否能被利用,而业务逻辑理解才决定该漏洞的实际危害。