原创内容,未获授权禁止转载、转发、抄袭。
接口测试时,我们经常会测必填、类型、长度、边界值。但有一类问题很容易被忽略。
就是参数污染。
下面的示例只适用于授权测试环境。
1. 什么是参数污染
HTTP 参数污染,简称 HPP。
简单说,就是攻击者在同一个请求里传多个同名参数,利用后端对参数解析方式的差异,影响业务逻辑。
比如:
username=test_user&username=admin或者 JSON 请求体里出现重复字段:
{"username":"test_user","username":"admin","password":"******"}重点不是请求能不能发出去。
而是后端最终取了哪个值。
这类测试最好用 Burp、Charles、Postman raw body 或curl构造原始请求。
不要只在可视化参数表格里改。
有些工具会自动去重,导致测试请求根本没把重复字段发出去。
核心原理
Query、Form 这类参数层面,同名参数可能会被传到服务端。
JSON 重复字段则更依赖解析器和框架实现。
不同后端框架,对重复参数或重复字段的处理方式不一样。
- 有的取第一个值
- 有的取最后一个值
- 有的拼接成字符串
- 有的解析成数组
如果业务代码没有显式校验,就可能出现非预期结果。
参数污染本身不一定是漏洞。
但如果影响登录、支付、退款、权限、金额,就是高风险问题。
危害场景
常见风险主要有两类。
第一类是业务逻辑漏洞。
比如登录成错误用户、支付金额被篡改、退款金额异常、订单状态被错误更新。
第二类是绕过安全机制。
比如绕过输入校验、绕过权限判断,或者配合其他漏洞扩大影响范围。
下面用两个例子说明。
2. 例子1:登录接口参数污染
正常登录请求大概是这样:
{"username":"test_user","password":"******","institutionId":"ORG_ID"}测试时,可以把请求体改成:
{"username":"test_user","username":"admin","password":"******","institutionId":"ORG_ID"}这时不要只看接口是否返回成功。
要继续验证最终登录身份。
- token 里是谁
- 当前用户接口返回是谁
- 页面展示的用户是谁
- 会话表保存的是谁
- 操作日志记录的是谁
如果用普通用户密码,最后登录成了管理员。
这就不是普通参数问题。
这是身份认证逻辑缺陷。
进一步验证
还要把两个username的顺序调换:
{"username":"admin","username":"test_user","password":"******","institutionId":"ORG_ID"}如果调换顺序后,登录身份也跟着变化。
说明后端很可能采用了“第一个值”或“最后一个值”的解析策略。
测试结论不要写:
登录接口返回成功。而要写:
登录接口存在重复 username 参数解析风险,调换字段顺序后登录身份发生变化,可能导致身份认证被绕过。漏洞原理分析
这里通常有几种可能。
第一种,覆盖策略。
后端取最后一个username,导致前面的值被覆盖。
第二种,首值策略。
后端只取第一个username,后面的值被忽略。
第三种,数组策略。
解析器把重复字段处理成数组。
如果业务代码期望字符串,但实际拿到数组,又没有做类型校验,就可能出现异常逻辑。
测试时要把解析策略写清楚。
不要只记录“成功”或“失败”。
3. 例子2:支付接口金额污染
支付接口更危险。
因为参数污染一旦影响金额,就可能造成直接经济损失。
正常支付请求:
{"orderId":"ORDER_ID","model":1,"paymentDetailList":[{"type":"PAY_TYPE","actualAmount":99.00,"changeAmount":0}]}污染后的请求:
{"orderId":"ORDER_ID","model":1,"paymentDetailList":[{"type":"PAY_TYPE","actualAmount":99.00,"actualAmount":1.00,"changeAmount":0}]}还可以继续验证极端金额场景。
比如 0 元、负数、小数精度异常等。
这类场景要重点验证:
- 实际支付金额是多少
- 支付流水金额是多少
- 订单原始金额是否被改动
- 订单状态是否变成已支付
- 财务记录是否一致
- 是否出现低价买入、0 元购、倒贴金额
支付接口不能信任前端传来的金额。
服务端必须重新计算订单金额。
漏洞根因
这类问题一般不是单点原因。
常见根因有三个。
第一,后端解析逻辑没有做重复参数检查。
比如框架把 JSON 自动绑定成对象时,默认接受了重复字段。
publicclassPaymentRequest{privateList<PaymentDetail>paymentDetailList;}publicclassPaymentDetail{privateBigDecimalactualAmount;}如果actualAmount重复出现,框架可能取第一个,也可能取最后一个。
第二,金额验证缺失。
后端没有把前端传入金额和服务端订单金额做比对。
第三,业务流程设计有问题。
比如:
- 创建订单时,后端计算金额为 99.00
- 用户发起支付时,前端提交金额被篡改为 1.00
- 后端只判断支付成功,没有校验支付金额是否等于订单金额
- 订单状态直接变成已支付
这就是典型的业务一致性问题。
4. 安全危害
参数污染的危害,取决于它影响了哪个业务字段。
如果影响登录,可能导致账户接管。
如果影响权限,可能导致越权访问。
如果影响金额,可能导致直接经济损失。
如果影响订单状态,可能导致业务流程错乱。
尤其是这些字段,需要重点关注:
- username
- userId
- roleId
- tenantId
- orderId
- amount
- actualAmount
- couponId
- status
这些字段一旦被污染,影响的通常不是页面展示。
而是身份、金额、权限和数据归属。
5. 修复建议
第一,严格输入校验。
同一个字段只允许出现一次。
发现重复参数,直接拒绝。
400 Bad Request第二,关键参数不要信任客户端。
金额、折扣、优惠、订单状态,必须由服务端计算。
第三,身份和权限从服务端获取。
用户身份应该来自 token 或服务端会话。
不能信任前端传入的userId、username、roleId。
第四,支付流程要做双重校验。
- 支付前校验订单状态
- 支付前校验订单金额
- 支付后校验支付流水
- 异步任务核对订单和支付记录
- 监控异常金额订单
第五,保留审计日志。
支付、退款、权限变更类接口,要记录原始请求、解析后参数和最终业务结果。
总结
参数污染测试,不是看接口能不能接收重复参数。
而是看重复参数有没有改变业务结果。
登录场景,要追最终身份。
支付场景,要追订单、流水和财务记录。
权限场景,要追真实数据访问范围。
只要参数能影响身份、金额、权限,就不能只看接口返回。
要一直追到数据库、日志和最终业务状态。