PHP项目越权访问漏洞如何排查修复

wen PHP项目 30

本文目录导读:

PHP项目越权访问漏洞如何排查修复

  1. 排查步骤
  2. 修复方案
  3. 快速排查清单
  4. 测试验证

针对PHP项目中的越权访问漏洞(分为水平越权:同级别用户越权操作其他用户数据;垂直越权:低权限用户操作高权限功能),以下是系统的排查与修复指南。


排查步骤

代码审计:定位关键入口

重点检查 API 接口控制器方法后台管理文件,典型特征:

  • 通过 $_GET$_POST$_REQUEST 获取用户 ID、订单号、资源 ID 等参数
  • 直接使用参数查询数据库,如 SELECT * FROM orders WHERE id = $_GET['order_id']
  • 未对 $_SESSION$_SERVER['REQUEST_URI'] 等来源的权限做校验

权限校验点检查

  • 检查所有涉及数据读写的代码块:是否在操作前校验当前用户对目标资源的归属权/授权
  • 检查角色/权限判断函数:如 isAdmin()checkPermission() 是否被遗漏或实现有漏洞
  • 检查文件包含:是否存在未校验权限的后台文件直接可被访问(如 /admin/export.php 未鉴权)

动态测试(黑盒+白盒)

  • 手动测试:修改请求参数(URL、POST数据),模拟不同用户访问
    • 水平越权测试:登录用户 A,将请求中的 user_id=123 改为 user_id=456
    • 垂直越权测试:普通用户直接访问 /admin/user_list,观察返回
  • 自动化工具:使用 Burp Suite 的 Authz/Auth Analyzer 插件自动对比未授权与授权请求的响应差异

常见漏洞模式

  • 直接对象引用(IDOR):如 delete.php?file_id=333 未验证文件归属
  • 未做角色路由限制:admin 路由未在框架层面拦截,普通用户可访问
  • 权限判断依赖前端:前端隐藏按钮但后端未校验
  • Session 或 Token 未绑定用户:仅检查 token 未过期,未验证 token 所属用户

修复方案

建立统一权限校验层

推荐使用 中间件AOP(面向切面编程),避免在每个方法中重复写校验代码。

ThinkPHP 中间件示例:

// app/middleware/AuthCheck.php
public function handle($request, \Closure $next)
{
    $user = session('user');
    if (!$user) {
        return redirect('/login');
    }
    // 检查请求路径与用户角色匹配
    $route = $request->pathinfo();
    $permMap = [
        'admin/*'   => ['admin'],
        'user/*'    => ['user', 'admin'],
    ];
    if (!$this->checkRoutePerm($route, $user['role'], $permMap)) {
        return response('权限不足', 403);
    }
    return $next($request);
}

水平越权:资源归属校验

操作任何用户关联数据时,必须验证当前用户与资源所有者的关系。

正确示例(基于当前用户ID):

// 修复前(危险)
$orderId = $_GET['order_id'];
$order = DB::table('orders')->where('id', $orderId)->first();
return $order;
// 修复后(加用户ID条件)
$userId = session('user_id');
$order = DB::table('orders')
    ->where('id', $orderId)
    ->where('user_id', $userId)   // 关键:限制只能查自己的
    ->first();
if (!$order) {
    return response('订单不存在或无权限', 403);
}

复杂场景(如群组资源):

// 检查用户是否在群组成员中
$file = File::find($fileId);
if (!$file->group->users->contains('id', Auth::id())) {
    abort(403, '无权访问此文件');
}

垂直越权:RBAC(基于角色的访问控制)

定义角色-权限映射表,每次执行敏感操作前校验。

数据库结构:

-- 用户角色表
CREATE TABLE roles (id INT, name VARCHAR(50));
-- 权限表
CREATE TABLE permissions (id INT, name VARCHAR(50), code VARCHAR(50));
-- 角色-权限关联表
CREATE TABLE role_permission (role_id INT, permission_id INT);

代码校验:

function checkPermission($permissionCode) {
    $user = session('user');
    $roleId = $user['role_id'];
    $hasPerm = DB::table('role_permission')
        ->join('permissions', 'role_permission.permission_id', '=', 'permissions.id')
        ->where('role_permission.role_id', $roleId)
        ->where('permissions.code', $permissionCode)
        ->exists();
    if (!$hasPerm) {
        throw new Exception('权限不足');
    }
}

其他关键修复点

  • 禁用直接对象引用:不使用可猜测的连续ID(如1,2,3),改用UUID或哈希ID
    // 生成唯一资源标识
    $fileUuid = Str::uuid();
    // 查询时使用 uuid 而非 id 作为参数
  • 后端强制校验所有敏感操作:不要相信前端传递的任何权限标记(如 is_admin=1
  • 参数化查询:防止SQL注入(但与越权无关,但需同时修复)
  • 日志监控:记录异常访问行为(如某用户频繁修改他人ID参数)
  • 框架全局防护:使用 Laravel 的 Gate / Policy 或 ThinkPHP 的 @authorize 注解

快速排查清单

步骤 检查项 工具/方法
1 所有带ID参数的接口 grep -r "\$_GET['.*id']" /app/
2 SQL语句中是否使用当前用户ID作为条件 代码审查
3 admin目录是否被Nginx正确拦截 curl http://domain/admin/xxx --cookie "session=xxx"
4 角色路由注册是否完整 检查路由文件如 routes/admin.php
5 文件上传/删除操作 检查文件路径是否被篡改(如 路径遍历)

测试验证

修复完成后进行回归测试:

  1. 水平越权:登录用户 tom,尝试访问 /api/order/list?user_id=jerry,应返回 403 或空结果
  2. 垂直越权:普通用户直接 POST 到 /api/admin/deleteUser,应被中间件拦截
  3. 批量越权:使用 Burp Intruder 枚举 id=1..1000 的接口,查看是否有非本人数据返回

抱歉,评论功能暂时关闭!