安全测试-03-参数篡改、重放与接口滥用场景怎么发现

业务测试里,最容易被忽略的一类安全问题,不是特别“黑客感”的漏洞,而是这些更朴素的失守:

  • 前端限制住的字段,后端其实照单全收
  • 一次性的请求,被重复打很多次还能持续生效
  • 原本只给单用户偶尔使用的接口,被脚本批量跑起来后系统完全没防护

这些问题看起来不复杂,但落到真实业务里,风险往往非常直接:

  • 金额被改了
  • 订单被重复提交了
  • 优惠券被反复领了
  • 验证码被打爆了
  • 导出接口被批量刷穿了

所以如果要在安全测试里优先抓一类问题,通常会优先看这三件事:

  • 参数篡改
  • 请求重放
  • 接口滥用

这篇文章就围绕这三类问题,讲一套更适合业务测试落地的发现方法。

一、先明确三类问题分别是什么

1. 参数篡改

本质是:

客户端传上来的参数,本来不应该被完全信任,但服务端真的信了。

常见场景:

  • 金额、折扣、数量
  • 角色、状态、审批结果
  • 文件路径、导出范围
  • 用户 ID、部门 ID、资源归属 ID

2. 请求重放

本质是:

一个原本只该生效一次,或者只该在某个时间窗内生效的请求,被重复使用了。

常见场景:

  • 支付回调
  • 审批动作
  • 下单提交
  • 领取优惠券
  • 发送验证码

3. 接口滥用

本质是:

接口设计默认按“正常用户偶发使用”考虑,但没有限制住批量调用、异常频率、异常顺序和异常规模。

常见场景:

  • 注册、登录、短信发送
  • 查询、导出、搜索
  • 批量创建、批量删除
  • 上传、下载、回调

把这三类问题分开很重要。
因为虽然它们经常一起出现,但排查和修复点不完全一样。

二、哪些接口最值得优先测

如果时间有限,我优先看下面这些动作型接口:

  • 下单、支付、退款、充值
  • 审批、驳回、删除、封禁
  • 领券、核销、积分变更
  • 发送验证码、发送通知、触发任务
  • 导出、下载、批量查询
  • 回调、Webhook、内部运维接口

为什么这些接口优先级高?

因为它们通常同时具备下面至少一个属性:

  • 能改钱
  • 能改状态
  • 能改权限
  • 能批量消耗资源
  • 能触发下游动作

只要具备这些属性,参数篡改、重放和滥用的收益就很高,风险也会很直接。

三、更常用的一套检查顺序

1. 先正常走业务链路

不要上来就乱改包。
先把业务动作正常跑一遍,明确:

  • 哪个请求是真正的写操作
  • 哪些参数看起来像“业务核心字段”
  • 哪些头、Token、签名、时间戳在变化

例如做“领券”流程时,先记录:

  • 用户点击领券时到底发了哪一个请求
  • 请求体里有没有 couponIduserIdcountchannel
  • 是否带 noncetimestampsign

2. 再判断字段谁该可信,谁不该可信

这是参数篡改测试的关键。

通常会把字段先分成三类:

  • 展示型字段:改了通常没意义
  • 业务型字段:金额、数量、状态、角色、资源 ID
  • 防护型字段:签名、时间戳、nonce、token

最值得优先试的,永远是业务型字段。

3. 再判断请求是不是天然有“一次性”要求

这一步是重放测试的关键。

如果一个请求满足下面任一条件,就应该主动怀疑它是否需要防重:

  • 会扣钱
  • 会改库存
  • 会改状态
  • 会发券
  • 会触发审批
  • 会触发短信或回调

4. 最后再看接口能不能被批量、异常频率、异常顺序使用

这一步是滥用测试的关键。

例如:

  • 一分钟发 200 次验证码会怎样
  • 同一个导出接口并发跑 30 个任务会怎样
  • 同一个用户连续发 500 次搜索请求会怎样
  • 正常应该“先登录再提交”的流程,如果直接跳过前置步骤会怎样

四、参数篡改最小执行骨架

如果要总结一套最小可执行方法,可以这样做。

1. 先记一张字段风险表

参数 来源 业务含义 理论上谁该决定 首测动作
amount 请求体 支付金额 服务端订单系统 改小/改大
role 请求体 用户角色 服务端权限系统 提升角色
status 请求体 审批结果 服务端流程引擎 直接改通过
deptId 请求体 数据范围 服务端数据权限 换部门
userId 请求体 操作者身份 服务端登录态 换人

这张表非常有用。
因为很多时候你只要看一眼,就知道哪些字段不应该由前端说了算。

2. 每次只改一个关键字段

例如:

  • amount=100 改成 1
  • count=1 改成 10
  • status=draft 改成 approved
  • role=user 改成 admin

一次只改一个,才能知道系统到底信了哪一个字段。

3. 同时确认最终业务结果

不能只看响应体。

要同时确认:

  • 页面状态有没有变化
  • 列表数据有没有变化
  • 数据库或日志里有没有真实落库
  • 下游动作有没有被触发

否则很容易把“接口回包松”误判成“真实漏洞”。

五、请求重放最小执行骨架

1. 先识别哪些动作应该天然防重

重点接口通常包括:

  • 下单
  • 支付确认
  • 审批通过
  • 退款提交
  • 领券
  • 发送验证码

2. 固定 3 组重放动作

通常每个关键接口至少做这 3 组:

  1. 原请求立即重复一次
  2. 原请求间隔几十秒再重复一次
  3. 复制原请求并发重复多次

这三组能快速看出:

  • 有没有幂等控制
  • 有没有时间窗限制
  • 并发下是否会重复落库

3. 重点观察这些结果

  • 是否重复创建订单
  • 是否重复发券
  • 是否重复扣库存
  • 是否重复发送短信
  • 是否重复触发回调

很多问题不是响应不对,而是下游被多次触发。

六、接口滥用最小执行骨架

接口滥用最怕测成“纯压测”,那样结论会很虚。
更倾向按业务约束去看。

1. 先问接口正常使用假设是什么

例如:

  • 登录接口默认一个用户一分钟不会失败 500 次
  • 验证码接口默认一个手机号一分钟不会发 30 次
  • 导出接口默认一个用户不会同时导出 20 份大报表

2. 再刻意打破这个假设

例如:

  • 单账号高频失败登录
  • 同手机号重复发码
  • 同用户并发导出
  • 同资源批量刷详情

3. 观察系统有没有最基本的边界保护

至少要看:

  • 频控
  • 限流
  • 验证码/图形码
  • 幂等
  • 队列削峰
  • 单用户/单资源限制

如果这些都没有,接口就很容易从“正常业务能力”变成“可被脚本滥用的入口”。

七、最容易踩的几个坑

1. 只改参数,不理解参数背后的业务含义

例如把 status=approved 改成功了,问题不只是“参数可改”,更可能说明流程状态机完全没有在服务端兜住。

2. 只看接口回包,不看后续副作用

一个请求重复打两次,第二次也许回包是失败。
但如果短信已经发了两次、库存已经扣了两次,那问题依然成立。

3. 把测试环境宽松配置当成正式问题

例如测试环境关闭了签名、关闭了频控、短信走了假通道。
这种情况需要单独标注,否则优先级会失真。

4. 把接口滥用测成纯性能问题

接口滥用不是单纯看“扛不扛得住”,而是看:

  • 不该被这样用时,系统有没有边界保护
  • 被这样用后,会不会产生业务性损失

八、真实案例:为什么“每人限领一次”的优惠券被领了三次

场景

一个活动系统里,运营配置了一张优惠券,规则是“每个用户限领一次”。
前端页面上,用户领取成功后按钮会变灰,页面提示“已领取”。

功能测试时,这条流程表现正常。

执行

我在第一次领券时抓到了请求:

1
2
3
4
5
6
7
POST /api/coupon/receive HTTP/1.1
Authorization: Bearer xxxx
Content-Type: application/json

{
"couponId": "CP20210401008"
}

然后做了两组动作:

  1. 在第一次领取成功后,立即重放相同请求两次
  2. 再开两个并发请求同时重放

现象

接口并不是每次都返回成功。
但在最终账户里,这个用户实际拿到了 3 张券。

也就是说:

  • 页面按钮置灰了
  • 前端看起来拦住了
  • 部分回包还提示失败

但服务端的发券动作在并发和重复请求下还是执行了多次。

排查

后面顺着日志看,问题链路很清楚:

  • 服务端先查“用户是否已领券”
  • 如果没有,就插入领券记录
  • 但这两个动作之间没有真正的原子控制
  • 并发请求同时进来时,多个请求都在“还没看到已有记录”这个窗口里通过了校验

这本质上不是前端问题,而是:

  • 没有幂等令牌
  • 没有唯一约束兜底
  • 没有并发条件下的防重设计

修复

最后给出的修复建议分了 4 层:

  1. 发券动作增加幂等控制
  2. 数据库对“用户 + 优惠券”增加唯一约束
  3. 并发冲突时返回明确的“已领取”语义
  4. 增加自动化回归用例:
  • 同一用户连续重放领券请求
  • 同一用户并发重放领券请求
  • 预期最终只生成一条有效领券记录

这个案例很典型地说明:

“页面按钮灰了”不代表服务端真的守住了。

九、怎么把这类问题持续沉淀下来

更合适的做法是把这三类检查固化成上线前的固定动作。

第一层:手工检查清单

高风险接口固定检查:

  • 关键业务参数能不能被改
  • 关键动作能不能被重复打
  • 高频接口有没有最基础频控
  • 批量接口有没有被异常规模调用的空间

第二层:自动化回归

把下面这些最关键的场景做成脚本:

  • 重放领券/审批/下单请求
  • 改金额、改数量、改状态
  • 并发重放一次性动作
  • 高频请求打登录、短信、导出类接口

这样才能避免系统每改一轮就把老问题重新带回来。

十、写在最后

参数篡改、重放、接口滥用之所以值得优先抓,不是因为它们听起来专业,而是因为它们离真实业务损失非常近。

很多系统安全问题,最后都能落回一句很朴素的话:

  • 不该由客户端决定的,被客户端决定了
  • 不该重复执行的,被重复执行了
  • 不该被大规模使用的,被大规模滥用了

如果这三条边界没守住,再多的安全术语也没有太大意义。