第四章:水平越权测试:基于ID的参数遍历、批量分配漏洞、UUID可预测性分析

好,咱们进入第四章。水平越权,说白了就是「你能看到隔壁老王的数据」。这在API测试里太常见了。我做过那么多渗透项目,十个里面至少有六七个存在这类问题。今天咱们就把这块彻底讲透。

4.1 水平越权的本质

先理清概念。垂直越权是「普通用户干管理员的事」,水平越权是「用户A看用户B的数据」。两者都危险,但水平越权更隐蔽,也更难防御。

为什么会这样?因为很多开发人员觉得「只要用户登录了,就安全了」。他们忘了——登录只能证明你是谁,不能证明你该看什么。

我举个例子。一个电商API:

GET /api/orders/1001
Authorization: Bearer user_token_123

如果后端只校验token是否有效,没校验这个订单是否属于当前用户,那我把1001改成1002,就能看到别人的订单了。就这么简单。

⚠️ 注意: 水平越权测试的核心思路就一句话——「我能不能访问不属于我的资源?」。所有测试方法都围绕这个展开。

4.2 基于ID的参数遍历

这是最基础,也是最常见的攻击方式。API里到处都是ID:用户ID、订单ID、商品ID、文章ID……只要这些ID是数字且递增,就存在遍历风险。

4.2.1 数字ID遍历

我在项目中遇到过这样一个案例。一个医疗平台的API:

GET /api/v1/patient/records/23456

返回了患者的病历信息。我试着把23456改成23457,居然也能访问。再改成23458,还是能访问。好家伙,整个数据库的病历全暴露了。

测试方法很简单:

  1. 先正常请求,拿到自己的资源ID
  2. 把ID加1或减1,看能不能访问
  3. 批量遍历,比如从1到10000
  4. 观察响应状态码和数据内容

用Burp Suite的Intruder模块,几秒钟就能跑完。但要注意——别跑太快,小心把人家服务搞挂了。

💡 技巧: 我习惯用Burp的「Numbers」payload类型,设置从1到10000,步长1。然后看响应长度,长度异常的很可能就是有效数据。

4.2.2 复合ID遍历

有些API用了复合ID,比如:

GET /api/orders/2024/03/1001

这种看起来复杂,其实一样。日期部分可以遍历,订单号部分也可以遍历。我见过一个系统,日期是用户注册日期,订单号是自增的。结果呢?攻击者只要知道目标用户的注册日期,就能遍历他的所有订单。

4.3 批量分配漏洞

这个有意思。批量分配漏洞(Mass Assignment),也叫「自动绑定漏洞」。它跟参数遍历不太一样,但同样危险。

4.3.1 原理

很多框架(比如Spring、Django、Laravel)支持自动将请求参数绑定到对象属性。如果开发人员没做限制,攻击者就能通过额外参数修改不该改的字段。

举个例子。用户注册API:

POST /api/register
{
  "username": "test",
  "password": "123456",
  "email": "test@test.com"
}

如果后端直接绑定到User对象,我加个参数试试:

POST /api/register
{
  "username": "test",
  "password": "123456",
  "email": "test@test.com",
  "role": "admin"
}

你猜怎么着?有些系统真的会把这个用户创建成管理员。我在一次测试中就遇到过,直接注册了个管理员账号,然后……嗯,后面的你懂的。

4.3.2 测试方法

测试批量分配漏洞,我一般这么干:

  1. 先看API文档或抓包,了解所有参数
  2. 猜测可能的隐藏字段:role、is_admin、is_verified、balance、credit
  3. 在请求里加上这些字段,看响应有没有变化
  4. 重点关注创建和更新接口
🔍 重点关注: PUT和PATCH请求最容易出现批量分配漏洞。因为开发人员经常用「全量更新」的方式,直接把请求体映射到数据库字段。

4.4 UUID可预测性分析

很多人觉得「用了UUID就安全了」。嗯,这话对了一半。UUID确实比自增ID安全,但前提是——你得用对版本。

4.4.1 UUID版本

UUID有5个版本,咱们重点看前三个:

版本 生成方式 可预测性
v1 基于时间戳+MAC地址 高
v4 随机数 低
v5 基于命名空间+哈希 中

v1 UUID的问题在于——它包含时间戳。如果你拿到一个v1 UUID,就能推算出生成时间。如果再拿到几个,就能推测出生成规律。

我曾经遇到一个系统,用的就是v1 UUID。我收集了100个UUID,解析出时间戳,发现它们之间的间隔很规律——每10毫秒生成一个。然后我就能预测下一个UUID大概是什么时候生成,再结合一些其他信息,就能猜出具体的UUID值。

4.4.2 测试方法

测试UUID可预测性,我一般分三步:

  1. 识别版本:看UUID的格式。v1 UUID的第13位是1,v4 UUID的第13位是4
  2. 收集样本:至少收集50-100个UUID,记录生成顺序和时间
  3. 分析规律:用工具(比如uuid-analyzer)分析时间戳分布
💡 工具推荐: 我常用一个Python脚本,用uuid库解析UUID的时间戳。再配合pandas做统计分析,很快就能看出规律。

4.4.3 实战案例

说个真实的。一个金融平台的交易记录API:

GET /api/transactions/{uuid}

UUID看起来是随机字符串。但我抓了几个包,发现UUID的第13位是1——v1版本。我写了个脚本,解析出时间戳,发现这些交易记录是按秒生成的。然后我根据当前时间,往前推了24小时,生成了所有可能的UUID。结果呢?我拿到了当天所有的交易记录,包括别人的。

⚠️ 重要提醒: 如果你发现系统用了v1 UUID,一定要检查时间戳的精度。精度越高(比如毫秒级),可预测性越低。但如果是秒级,基本等于没防护。

4.5 综合防御建议

讲了这么多攻击方法,咱们也得说说怎么防。我个人的经验是:

  • 参数遍历:用UUID v4替代自增ID,或者加权限校验
  • 批量分配:用白名单机制,只允许修改指定字段
  • UUID可预测性:用v4版本,别用v1

但说实话,最根本的防御就一条——每次访问资源时,都校验当前用户是否有权限。别偷懒,别相信客户端传来的任何数据。

嗯,这一章内容不少。水平越权测试看起来简单,但真正做好不容易。我建议你多动手,多抓包,多分析。下一章咱们聊垂直越权,那个更刺激。