本文目录导读:

针对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,观察返回
- 水平越权测试:登录用户 A,将请求中的
- 自动化工具:使用 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 | 文件上传/删除操作 | 检查文件路径是否被篡改(如 路径遍历) |
测试验证
修复完成后进行回归测试:
- 水平越权:登录用户 tom,尝试访问
/api/order/list?user_id=jerry,应返回 403 或空结果 - 垂直越权:普通用户直接 POST 到
/api/admin/deleteUser,应被中间件拦截 - 批量越权:使用 Burp Intruder 枚举 id=1..1000 的接口,查看是否有非本人数据返回
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播