用gemini修复php越权,先把“谁能对哪条数据做什么”写成规则,再让ai检查每个入口是否真正执行这些规则。下面用多租户文档修改演示服务端身份、资源归属和拒绝请求测试。

先捋顺权限矩阵,别一上来就直接让Gemini生成代码
越权漏洞最常出现在数据详情页、批量编辑、导出功能和各类后台接口里。你先把用户身份、所属租户、资源归属、允许操作这几个维度列清楚做成权限矩阵,明确普通用户是不是只能改自己上传的文档,管理员有没有额外的跨权限操作授权。
本文采用严格的本人文档规则,不设置“管理员自动放行”。如果业务存在共享协作或委托审批,应先增加明确规则和测试,再让Gemini接入,不能由模型根据角色名称猜测权限。
读取权限与修改权限也应分别列出。数据库返回零条更新可能表示资源不在允许范围,也可能只是值未变化;对外响应应按业务统一设计,不通过错误差异泄露他人文档是否存在。
别拿ID难猜、用户已登录当授权校验
认证回答请求者是谁,授权回答他能对目标资源执行什么动作。即使使用随机编号或已经登录,服务器仍需逐请求核对资源归属与租户边界。前端隐藏按钮只影响展示,不能阻止直接请求接口;
哪怕你做了SQL参数绑定,也只能防注入保护查询结构,根本不会自动判断业务权限。所有写操作一定要避免「先查一遍校验权限,再丢了条件去更新」的脱节问题,要把权限限制条件直接带到实际修改数据的逻辑里。
打开Gemini网页版,提交你整理好的权限规则和对应业务代码
打开Gemini网页版,登录可用账号并新建会话。把权限矩阵、脱敏路由、认证上下文生成方式和修改函数一起粘贴,必要时使用添加文件入口补充允许对外处理的材料。
gemini-image-generation下载使用 Google GenAI SDK 通过 Gemini 生成或编辑图像。当用户要求在 OpenCla… 中创建、转换、渲染或保存一张或多张图像时使用。
先让Gemini找出每个读取和写入入口的权限检查位置。要求区分同租户不同用户、不同租户以及未登录三类场景,不要只测试一个管理员账号。
要求给出最小差异,并解释当前身份是否可能被请求参数覆盖。检查补丁有没有把所有者限制删除、把客户端角色当成可信值,或仅仅给编号做了格式校验。
运行独立测试后,再用两个测试账号互换资源编号,验证详情、修改、删除和批量接口。每次拒绝都要核对数据库没有变化,不只检查返回状态码。
根据权限矩阵审查PHP文档接口的水平越权与跨租户访问。 当前用户及租户必须来自可信认证上下文,客户端仅能提交文档编号和允许修改的标题。 普通用户只能修改同租户且属于本人的文档,不增加隐含管理员放行。 把授权限制落实到实际更新条件,拒绝修改owner_id、tenant_id和权限字段。 给完整函数、最小差异及跨用户、跨租户、未登录和拒绝时数据不变的测试。 规则不清的共享场景请先列为待确认,不自行放宽。
完整参考代码:更新逻辑同时校验资源ID、租户归属和所有者身份
下面使用三条内存文档记录。第三条故意沿用相同所有者编号但属于另一租户,用来检查代码是否遗漏租户条件;真实身份数组必须由服务端认证层构造。
<?php declare(strict_types=1);
$db = new PDO('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
$db->exec('CREATE TABLE documents(id INTEGER PRIMARY KEY, tenant_id INTEGER, owner_id INTEGER, title TEXT)');
$insert = $db->prepare('INSERT INTO documents VALUES(?,?,?,?)');
foreach ([[101,10,7,'A'],[102,10,8,'B'],[103,20,7,'C']] as $row) { $insert->execute($row); }
function renameDocument(PDO $db, array $actor, int $id, array $payload): int {
// actor必须来自可信认证层,不允许从请求中的user_id、tenant_id构造。
if (!is_int($actor['id'] ?? null) || $actor['id'] 160 || $id prepare('UPDATE documents SET title=:title WHERE id=:id AND tenant_id=:tenant AND owner_id=:owner');
$stmt->execute(['title'=>$payload['title'],'id'=>$id,'tenant'=>$actor['tenant'],'owner'=>$actor['id']]);
return $stmt->rowCount();
}
function check(bool $value, string $name): void {
if (!$value) { throw new RuntimeException('FAIL: '.$name); }
echo $name, ': PASS', PHP_EOL;
}
$actor = ['id'=>7,'tenant'=>10,'permissions'=>['edit_own_document']];
check(renameDocument($db,$actor,101,['title'=>'Revised']) === 1, 'own_document');
check(renameDocument($db,$actor,102,['title'=>'Wrong']) === 0, 'same_tenant_other_owner');
check(renameDocument($db,$actor,103,['title'=>'Wrong']) === 0, 'other_tenant');
foreach (['owner_in_payload'=>[$actor,['title'=>'X','owner_id'=>7]],
'anonymous'=>[[],['title'=>'X']]] as $name=>[$identity,$payload]) {
try { renameDocument($db,$identity,102,$payload); }
catch (InvalidArgumentException $e) { echo $name, ': PASS rejected', PHP_EOL; continue; }
throw new RuntimeException('FAIL: '.$name);
}
check($db->query('SELECT title FROM documents WHERE id=102')->fetchColumn() === 'B' &&
$db->query('SELECT title FROM documents WHERE id=103')->fetchColumn() === 'C', 'denied_rows_unchanged');
echo 'Runtime: PHP ', PHP_VERSION, ' / ', PHP_SAPI, ' / PDO SQLite', PHP_EOL;
php audit.php
本次已在macOS上的PHP 8.5.10 WebAssembly和PDO SQLite执行六项检查。没有调用Gemini,没有连接生产库;样例不包含登录实现,不能把传入数组直接替换为用户提交字段。

个人私有资源和共享资源,权限规则的复杂度差异不小
| 业务 | 授权依据 | 测试重点 |
|---|---|---|
| 本人私有文档 | 租户与所有者均匹配 | 横向访问和跨租户访问被拒绝 |
| 团队共享文档 | 成员关系、动作权限和分享状态 | 成员移除、权限降级和分享失效 |
网页版对话只能分析你提供的上下文,不代表已读取整个项目。团队若使用Gemini Code Assist,应按对应企业产品的授权与文件上下文方式操作,不能把网页版步骤当成所有IDE版本的统一入口。
四个常见坑,稍不注意权限补丁就白忙活
- 单条修改安全,批量接口仍越权:逐项应用权限范围,不允许只验证列表第一条。
- 客户端可以指定角色:移除请求中的身份控制,权限信息只从可信服务端来源取得。
- 返回拒绝但数据已变化:检查更新条件和事务边界,补上数据库状态断言。
- 随机编号被当作安全措施:保留编号设计,但必须独立执行资源级授权检查。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










